wolfronix-sdk 1.3.2 → 2.3.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/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -27,6 +37,7 @@ __export(index_exports, {
27
37
  ValidationError: () => ValidationError,
28
38
  Wolfronix: () => Wolfronix,
29
39
  WolfronixError: () => WolfronixError,
40
+ WolfronixStream: () => WolfronixStream,
30
41
  createClient: () => createClient,
31
42
  default: () => index_default
32
43
  });
@@ -38,7 +49,7 @@ var getCrypto = () => {
38
49
  return globalThis.crypto;
39
50
  }
40
51
  throw new Error(
41
- "Web Crypto API not available. Requires a modern browser or Node.js 16+."
52
+ "Web Crypto API not available. Requires a modern browser or Node.js 18+."
42
53
  );
43
54
  };
44
55
  var RSA_ALG = {
@@ -213,6 +224,10 @@ async function rsaDecrypt(encryptedBase64, privateKey) {
213
224
  );
214
225
  return decrypted;
215
226
  }
227
+ async function rsaDecryptBase64(encryptedBase64, privateKey) {
228
+ const decrypted = await rsaDecrypt(encryptedBase64, privateKey);
229
+ return arrayBufferToBase64(decrypted);
230
+ }
216
231
  async function exportSessionKey(key) {
217
232
  return await getCrypto().subtle.exportKey("raw", key);
218
233
  }
@@ -325,6 +340,7 @@ var Wolfronix = class {
325
340
  this.config = {
326
341
  baseUrl: config,
327
342
  clientId: "",
343
+ wolfronixKey: "",
328
344
  timeout: 3e4,
329
345
  retries: 3,
330
346
  insecure: false
@@ -333,6 +349,7 @@ var Wolfronix = class {
333
349
  this.config = {
334
350
  baseUrl: config.baseUrl,
335
351
  clientId: config.clientId || "",
352
+ wolfronixKey: config.wolfronixKey || "",
336
353
  timeout: config.timeout || 3e4,
337
354
  retries: config.retries || 3,
338
355
  insecure: config.insecure || false
@@ -340,6 +357,10 @@ var Wolfronix = class {
340
357
  }
341
358
  this.config.baseUrl = this.config.baseUrl.replace(/\/$/, "");
342
359
  }
360
+ /** Expose private key status for testing */
361
+ hasPrivateKey() {
362
+ return this.privateKey !== null;
363
+ }
343
364
  // ==========================================================================
344
365
  // Private Helpers
345
366
  // ==========================================================================
@@ -350,6 +371,9 @@ var Wolfronix = class {
350
371
  if (this.config.clientId) {
351
372
  headers["X-Client-ID"] = this.config.clientId;
352
373
  }
374
+ if (this.config.wolfronixKey) {
375
+ headers["X-Wolfronix-Key"] = this.config.wolfronixKey;
376
+ }
353
377
  if (includeAuth && this.token) {
354
378
  headers["Authorization"] = `Bearer ${this.token}`;
355
379
  if (this.userId) {
@@ -375,6 +399,15 @@ var Wolfronix = class {
375
399
  headers,
376
400
  signal: controller.signal
377
401
  };
402
+ if (this.config.insecure && typeof process !== "undefined") {
403
+ try {
404
+ const { Agent } = await import("undici");
405
+ fetchOptions.dispatcher = new Agent({
406
+ connect: { rejectUnauthorized: false }
407
+ });
408
+ } catch {
409
+ }
410
+ }
378
411
  if (formData) {
379
412
  fetchOptions.body = formData;
380
413
  } else if (body) {
@@ -462,7 +495,7 @@ var Wolfronix = class {
462
495
  this.publicKey = keyPair.publicKey;
463
496
  this.privateKey = keyPair.privateKey;
464
497
  this.publicKeyPEM = publicKeyPEM;
465
- this.token = "session_" + Date.now();
498
+ this.token = "zk-session";
466
499
  }
467
500
  return response;
468
501
  }
@@ -497,7 +530,7 @@ var Wolfronix = class {
497
530
  this.publicKeyPEM = response.public_key_pem;
498
531
  this.publicKey = await importKeyFromPEM(response.public_key_pem, "public");
499
532
  this.userId = email;
500
- this.token = "session_" + Date.now();
533
+ this.token = "zk-session";
501
534
  return {
502
535
  success: true,
503
536
  user_id: email,
@@ -594,7 +627,14 @@ var Wolfronix = class {
594
627
  };
595
628
  }
596
629
  /**
597
- * Decrypt and retrieve a file
630
+ * Decrypt and retrieve a file using zero-knowledge flow.
631
+ *
632
+ * Flow:
633
+ * 1. GET /api/v1/files/{id}/key → encrypted key_part_a
634
+ * 2. Decrypt key_part_a client-side with private key (RSA-OAEP)
635
+ * 3. POST /api/v1/files/{id}/decrypt with { decrypted_key_a } in body
636
+ *
637
+ * The private key NEVER leaves the client.
598
638
  *
599
639
  * @example
600
640
  * ```typescript
@@ -607,7 +647,7 @@ var Wolfronix = class {
607
647
  * fs.writeFileSync('decrypted.pdf', buffer);
608
648
  * ```
609
649
  */
610
- async decrypt(fileId) {
650
+ async decrypt(fileId, role = "owner") {
611
651
  this.ensureAuthenticated();
612
652
  if (!fileId) {
613
653
  throw new ValidationError("File ID is required");
@@ -615,19 +655,20 @@ var Wolfronix = class {
615
655
  if (!this.privateKey) {
616
656
  throw new Error("Private key not available. Is user logged in?");
617
657
  }
618
- const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
658
+ const keyResponse = await this.getFileKey(fileId);
659
+ const decryptedKeyA = await rsaDecryptBase64(keyResponse.key_part_a, this.privateKey);
619
660
  return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
620
661
  responseType: "blob",
621
- headers: {
622
- "X-Private-Key": privateKeyPEM,
623
- "X-User-Role": "owner"
662
+ body: {
663
+ decrypted_key_a: decryptedKeyA,
664
+ user_role: role
624
665
  }
625
666
  });
626
667
  }
627
668
  /**
628
- * Decrypt and return as ArrayBuffer
669
+ * Decrypt and return as ArrayBuffer (zero-knowledge flow)
629
670
  */
630
- async decryptToBuffer(fileId) {
671
+ async decryptToBuffer(fileId, role = "owner") {
631
672
  this.ensureAuthenticated();
632
673
  if (!fileId) {
633
674
  throw new ValidationError("File ID is required");
@@ -635,15 +676,29 @@ var Wolfronix = class {
635
676
  if (!this.privateKey) {
636
677
  throw new Error("Private key not available. Is user logged in?");
637
678
  }
638
- const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
679
+ const keyResponse = await this.getFileKey(fileId);
680
+ const decryptedKeyA = await rsaDecryptBase64(keyResponse.key_part_a, this.privateKey);
639
681
  return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
640
682
  responseType: "arraybuffer",
641
- headers: {
642
- "X-Private-Key": privateKeyPEM,
643
- "X-User-Role": "owner"
683
+ body: {
684
+ decrypted_key_a: decryptedKeyA,
685
+ user_role: role
644
686
  }
645
687
  });
646
688
  }
689
+ /**
690
+ * Fetch the encrypted key_part_a for a file (for client-side decryption)
691
+ *
692
+ * @param fileId The file ID to get the key for
693
+ * @returns KeyPartResponse containing the RSA-OAEP encrypted key_part_a
694
+ */
695
+ async getFileKey(fileId) {
696
+ this.ensureAuthenticated();
697
+ if (!fileId) {
698
+ throw new ValidationError("File ID is required");
699
+ }
700
+ return this.request("GET", `/api/v1/files/${fileId}/key`);
701
+ }
647
702
  /**
648
703
  * List all encrypted files for current user
649
704
  *
@@ -688,11 +743,19 @@ var Wolfronix = class {
688
743
  /**
689
744
  * Get another user's public key (for E2E encryption)
690
745
  * @param userId The ID of the recipient
746
+ * @param clientId Optional: override the configured clientId
691
747
  */
692
- async getPublicKey(userId) {
748
+ async getPublicKey(userId, clientId) {
693
749
  this.ensureAuthenticated();
694
- const result = await this.request("GET", `/api/v1/keys/${userId}`);
695
- return result.public_key;
750
+ const cid = clientId || this.config.clientId;
751
+ if (!cid) {
752
+ throw new ValidationError("clientId is required for getPublicKey(). Set it in config or pass as second argument.");
753
+ }
754
+ const result = await this.request(
755
+ "GET",
756
+ `/api/v1/keys/public/${encodeURIComponent(cid)}/${encodeURIComponent(userId)}`
757
+ );
758
+ return result.public_key_pem;
696
759
  }
697
760
  /**
698
761
  * Encrypt a short text message for a recipient (Hybrid Encryption: RSA + AES)
@@ -745,6 +808,166 @@ var Wolfronix = class {
745
808
  }
746
809
  }
747
810
  // ==========================================================================
811
+ // Server-Side Message Encryption (Dual-Key Split)
812
+ // ==========================================================================
813
+ /**
814
+ * Encrypt a text message via the Wolfronix server (dual-key split).
815
+ * The server generates an AES key, encrypts the message, and splits the key —
816
+ * you get key_part_a, the server holds key_part_b.
817
+ *
818
+ * Use this for server-managed message encryption (e.g., stored encrypted messages).
819
+ * For true E2E (where the server never sees plaintext), use encryptMessage() instead.
820
+ *
821
+ * @param message The plaintext message to encrypt
822
+ * @param options.layer 3 = AES only (full key returned), 4 = dual-key split (default)
823
+ *
824
+ * @example
825
+ * ```typescript
826
+ * const result = await wfx.serverEncrypt('Hello, World!');
827
+ * // Store result.encrypted_message, result.nonce, result.key_part_a, result.message_tag
828
+ * ```
829
+ */
830
+ async serverEncrypt(message, options) {
831
+ this.ensureAuthenticated();
832
+ if (!message) {
833
+ throw new ValidationError("Message is required");
834
+ }
835
+ return this.request("POST", "/api/v1/messages/encrypt", {
836
+ body: {
837
+ message,
838
+ user_id: this.userId,
839
+ layer: options?.layer || 4
840
+ }
841
+ });
842
+ }
843
+ /**
844
+ * Decrypt a message previously encrypted via serverEncrypt().
845
+ *
846
+ * @param params The encrypted message data (from serverEncrypt result)
847
+ * @returns The decrypted plaintext message
848
+ *
849
+ * @example
850
+ * ```typescript
851
+ * const text = await wfx.serverDecrypt({
852
+ * encryptedMessage: result.encrypted_message,
853
+ * nonce: result.nonce,
854
+ * keyPartA: result.key_part_a,
855
+ * messageTag: result.message_tag,
856
+ * });
857
+ * ```
858
+ */
859
+ async serverDecrypt(params) {
860
+ this.ensureAuthenticated();
861
+ if (!params.encryptedMessage || !params.nonce || !params.keyPartA) {
862
+ throw new ValidationError("encryptedMessage, nonce, and keyPartA are required");
863
+ }
864
+ const response = await this.request(
865
+ "POST",
866
+ "/api/v1/messages/decrypt",
867
+ {
868
+ body: {
869
+ encrypted_message: params.encryptedMessage,
870
+ nonce: params.nonce,
871
+ key_part_a: params.keyPartA,
872
+ message_tag: params.messageTag || "",
873
+ user_id: this.userId
874
+ }
875
+ }
876
+ );
877
+ return response.message;
878
+ }
879
+ /**
880
+ * Encrypt multiple messages in a single round-trip (batch).
881
+ * All messages share one AES key (different nonce per message).
882
+ * Efficient for chat history encryption or bulk operations.
883
+ *
884
+ * @param messages Array of { id, message } objects (max 100)
885
+ * @param options.layer 3 or 4 (default: 4)
886
+ *
887
+ * @example
888
+ * ```typescript
889
+ * const result = await wfx.serverEncryptBatch([
890
+ * { id: 'msg1', message: 'Hello' },
891
+ * { id: 'msg2', message: 'World' },
892
+ * ]);
893
+ * // result.results[0].encrypted_message, result.key_part_a, result.batch_tag
894
+ * ```
895
+ */
896
+ async serverEncryptBatch(messages, options) {
897
+ this.ensureAuthenticated();
898
+ if (!messages || messages.length === 0) {
899
+ throw new ValidationError("At least one message is required");
900
+ }
901
+ if (messages.length > 100) {
902
+ throw new ValidationError("Maximum 100 messages per batch");
903
+ }
904
+ return this.request("POST", "/api/v1/messages/batch/encrypt", {
905
+ body: {
906
+ messages,
907
+ user_id: this.userId,
908
+ layer: options?.layer || 4
909
+ }
910
+ });
911
+ }
912
+ /**
913
+ * Decrypt a single message from a batch result.
914
+ * Uses the shared key_part_a and batch_tag from the batch result.
915
+ *
916
+ * @param batchResult The batch encrypt result
917
+ * @param index The index of the message to decrypt
918
+ */
919
+ async serverDecryptBatchItem(batchResult, index) {
920
+ if (index < 0 || index >= batchResult.results.length) {
921
+ throw new ValidationError("Invalid batch index");
922
+ }
923
+ const item = batchResult.results[index];
924
+ return this.serverDecrypt({
925
+ encryptedMessage: item.encrypted_message,
926
+ nonce: item.nonce,
927
+ keyPartA: batchResult.key_part_a,
928
+ messageTag: batchResult.batch_tag
929
+ });
930
+ }
931
+ // ==========================================================================
932
+ // Real-Time Streaming Encryption (WebSocket)
933
+ // ==========================================================================
934
+ /**
935
+ * Create a streaming encryption/decryption session over WebSocket.
936
+ * Data flows in real-time: send chunks, receive encrypted/decrypted chunks back.
937
+ *
938
+ * @param direction 'encrypt' for plaintext→ciphertext, 'decrypt' for reverse
939
+ * @param streamKey Required for decrypt — the key_part_a and stream_tag from the encrypt session
940
+ *
941
+ * @example
942
+ * ```typescript
943
+ * // Encrypt stream
944
+ * const stream = await wfx.createStream('encrypt');
945
+ * stream.onData((chunk, seq) => console.log('Encrypted chunk', seq));
946
+ * stream.send('Hello chunk 1');
947
+ * stream.send('Hello chunk 2');
948
+ * const summary = await stream.end();
949
+ * // Save stream.keyPartA and stream.streamTag for decryption
950
+ *
951
+ * // Decrypt stream
952
+ * const dStream = await wfx.createStream('decrypt', {
953
+ * keyPartA: stream.keyPartA!,
954
+ * streamTag: stream.streamTag!,
955
+ * });
956
+ * dStream.onData((chunk, seq) => console.log('Decrypted:', chunk));
957
+ * dStream.send(encryptedChunk1);
958
+ * await dStream.end();
959
+ * ```
960
+ */
961
+ async createStream(direction, streamKey) {
962
+ this.ensureAuthenticated();
963
+ if (direction === "decrypt" && !streamKey) {
964
+ throw new ValidationError("streamKey (keyPartA + streamTag) is required for decrypt streams");
965
+ }
966
+ const stream = new WolfronixStream(this.config, this.userId);
967
+ await stream.connect(direction, streamKey);
968
+ return stream;
969
+ }
970
+ // ==========================================================================
748
971
  // Metrics & Status
749
972
  // ==========================================================================
750
973
  /**
@@ -774,6 +997,186 @@ var Wolfronix = class {
774
997
  }
775
998
  }
776
999
  };
1000
+ var WolfronixStream = class {
1001
+ /** @internal */
1002
+ constructor(config, userId) {
1003
+ this.config = config;
1004
+ this.userId = userId;
1005
+ this.ws = null;
1006
+ this.dataCallbacks = [];
1007
+ this.errorCallbacks = [];
1008
+ this.pendingChunks = /* @__PURE__ */ new Map();
1009
+ this.seqCounter = 0;
1010
+ /** Client's key half (available after encrypt stream init) */
1011
+ this.keyPartA = null;
1012
+ /** Stream tag (available after encrypt stream init) */
1013
+ this.streamTag = null;
1014
+ }
1015
+ /** @internal Connect and initialize the stream session */
1016
+ async connect(direction, streamKey) {
1017
+ return new Promise((resolve, reject) => {
1018
+ const wsBase = this.config.baseUrl.replace(/^http/, "ws");
1019
+ const params = new URLSearchParams();
1020
+ if (this.config.wolfronixKey) {
1021
+ params.set("wolfronix_key", this.config.wolfronixKey);
1022
+ }
1023
+ if (this.config.clientId) {
1024
+ params.set("client_id", this.config.clientId);
1025
+ }
1026
+ const wsUrl = `${wsBase}/api/v1/stream?${params.toString()}`;
1027
+ this.ws = new WebSocket(wsUrl);
1028
+ this.ws.onopen = () => {
1029
+ const initMsg = { type: "init", direction };
1030
+ if (direction === "decrypt" && streamKey) {
1031
+ initMsg.key_part_a = streamKey.keyPartA;
1032
+ initMsg.stream_tag = streamKey.streamTag;
1033
+ }
1034
+ this.ws.send(JSON.stringify(initMsg));
1035
+ };
1036
+ let initResolved = false;
1037
+ this.ws.onmessage = (event) => {
1038
+ try {
1039
+ const msg = JSON.parse(event.data);
1040
+ if (msg.type === "error") {
1041
+ const err = new Error(msg.error);
1042
+ if (!initResolved) {
1043
+ initResolved = true;
1044
+ reject(err);
1045
+ }
1046
+ this.errorCallbacks.forEach((cb) => cb(err));
1047
+ return;
1048
+ }
1049
+ if (msg.type === "init_ack" && !initResolved) {
1050
+ initResolved = true;
1051
+ if (msg.key_part_a) this.keyPartA = msg.key_part_a;
1052
+ if (msg.stream_tag) this.streamTag = msg.stream_tag;
1053
+ resolve();
1054
+ return;
1055
+ }
1056
+ if (msg.type === "data") {
1057
+ this.dataCallbacks.forEach((cb) => cb(msg.data, msg.seq));
1058
+ const pending = this.pendingChunks.get(msg.seq);
1059
+ if (pending) {
1060
+ pending.resolve(msg.data);
1061
+ this.pendingChunks.delete(msg.seq);
1062
+ }
1063
+ return;
1064
+ }
1065
+ if (msg.type === "end_ack") {
1066
+ return;
1067
+ }
1068
+ } catch (e) {
1069
+ const err = new Error("Failed to parse stream message");
1070
+ this.errorCallbacks.forEach((cb) => cb(err));
1071
+ }
1072
+ };
1073
+ this.ws.onerror = (event) => {
1074
+ const err = new Error("WebSocket error");
1075
+ if (!initResolved) {
1076
+ initResolved = true;
1077
+ reject(err);
1078
+ }
1079
+ this.errorCallbacks.forEach((cb) => cb(err));
1080
+ };
1081
+ this.ws.onclose = () => {
1082
+ this.pendingChunks.forEach((p) => p.reject(new Error("Stream closed")));
1083
+ this.pendingChunks.clear();
1084
+ };
1085
+ });
1086
+ }
1087
+ /**
1088
+ * Send a data chunk for encryption/decryption.
1089
+ * Returns a promise that resolves with the processed (encrypted/decrypted) chunk.
1090
+ *
1091
+ * @param data String or base64-encoded binary data
1092
+ * @returns The processed chunk (base64-encoded)
1093
+ */
1094
+ async send(data) {
1095
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
1096
+ throw new Error("Stream not connected");
1097
+ }
1098
+ const b64Data = this.isBase64(data) ? data : btoa(data);
1099
+ const seq = this.seqCounter++;
1100
+ return new Promise((resolve, reject) => {
1101
+ this.pendingChunks.set(seq, { resolve, reject });
1102
+ this.ws.send(JSON.stringify({ type: "data", data: b64Data }));
1103
+ });
1104
+ }
1105
+ /**
1106
+ * Send raw binary data for encryption/decryption.
1107
+ *
1108
+ * @param buffer ArrayBuffer or Uint8Array
1109
+ * @returns The processed chunk (base64-encoded)
1110
+ */
1111
+ async sendBinary(buffer) {
1112
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
1113
+ let binary = "";
1114
+ for (let i = 0; i < bytes.byteLength; i++) {
1115
+ binary += String.fromCharCode(bytes[i]);
1116
+ }
1117
+ const b64 = btoa(binary);
1118
+ return this.send(b64);
1119
+ }
1120
+ /**
1121
+ * Register a callback for incoming data chunks.
1122
+ *
1123
+ * @param callback Called with (base64Data, sequenceNumber) for each chunk
1124
+ */
1125
+ onData(callback) {
1126
+ this.dataCallbacks.push(callback);
1127
+ }
1128
+ /**
1129
+ * Register a callback for stream errors.
1130
+ */
1131
+ onError(callback) {
1132
+ this.errorCallbacks.push(callback);
1133
+ }
1134
+ /**
1135
+ * End the stream session. Returns the total number of chunks processed.
1136
+ */
1137
+ async end() {
1138
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
1139
+ return { chunksProcessed: this.seqCounter };
1140
+ }
1141
+ return new Promise((resolve) => {
1142
+ const originalHandler = this.ws.onmessage;
1143
+ this.ws.onmessage = (event) => {
1144
+ try {
1145
+ const msg = JSON.parse(event.data);
1146
+ if (msg.type === "end_ack") {
1147
+ resolve({ chunksProcessed: msg.chunks_processed || this.seqCounter });
1148
+ this.ws.close();
1149
+ return;
1150
+ }
1151
+ } catch {
1152
+ }
1153
+ if (originalHandler && this.ws) originalHandler.call(this.ws, event);
1154
+ };
1155
+ this.ws.send(JSON.stringify({ type: "end" }));
1156
+ setTimeout(() => {
1157
+ resolve({ chunksProcessed: this.seqCounter });
1158
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
1159
+ this.ws.close();
1160
+ }
1161
+ }, 5e3);
1162
+ });
1163
+ }
1164
+ /**
1165
+ * Close the stream immediately without sending an end message.
1166
+ */
1167
+ close() {
1168
+ if (this.ws) {
1169
+ this.ws.close();
1170
+ this.ws = null;
1171
+ }
1172
+ this.pendingChunks.forEach((p) => p.reject(new Error("Stream closed")));
1173
+ this.pendingChunks.clear();
1174
+ }
1175
+ isBase64(str) {
1176
+ if (str.length % 4 !== 0) return false;
1177
+ return /^[A-Za-z0-9+/]*={0,2}$/.test(str);
1178
+ }
1179
+ };
777
1180
  function createClient(config) {
778
1181
  return new Wolfronix(config);
779
1182
  }
@@ -787,5 +1190,6 @@ var index_default = Wolfronix;
787
1190
  ValidationError,
788
1191
  Wolfronix,
789
1192
  WolfronixError,
1193
+ WolfronixStream,
790
1194
  createClient
791
1195
  });