react-native-mosquito-transport 0.0.47 → 0.0.48

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/TODO CHANGED
@@ -25,3 +25,11 @@
25
25
  - transform undefined to void instead of null
26
26
  - add metadata to provider auth login
27
27
  <!-- - error: "refreshToken retry limit exceeded" <--- no need -->
28
+
29
+ - _areYouOk() should timeout max 5000
30
+ - .json() should be parse nicely
31
+ - when server is offline and suddenly comes online, fetchHttp throw some error
32
+ - string to Buffer may be in utf16
33
+
34
+ # changes made
35
+ storage uploadFile method and others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-mosquito-transport",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "description": "React native javascript sdk for mosquito-transport (https://github.com/brainbehindx/mosquito-transport)",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@turf/turf": "^7.2.0",
31
31
  "buffer": "^6.0.3",
32
- "entity-serializer": "^1.0.3",
32
+ "entity-serializer": "^1.0.4",
33
33
  "guard-object": "^1.1.4",
34
34
  "poke-object": "^1.0.1",
35
35
  "simplify-error": "^1.0.1",
package/src/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { BSON } from "./vendor/bson";
2
2
 
3
- interface RNMTConfig {
3
+ export interface RNMTConfig {
4
4
  dbName?: string;
5
5
  dbUrl?: string;
6
6
  projectUrl: string;
@@ -38,7 +38,7 @@ interface RNMTConfig {
38
38
  castBSON?: boolean;
39
39
  }
40
40
 
41
- interface GetDatabase {
41
+ export interface GetDatabase {
42
42
  collection: (path: string) => RNMTCollection;
43
43
  }
44
44
 
@@ -98,7 +98,7 @@ type auth_provider_id_values = auth_provider_id['GOOGLE'] |
98
98
  interface ReleaseCacheOption_IO {
99
99
  }
100
100
 
101
- interface ReleaseCacheOption {
101
+ export interface ReleaseCacheOption {
102
102
  /**
103
103
  * fs is used as the default caching mechanism
104
104
  *
@@ -137,7 +137,7 @@ interface ReleaseCacheOption {
137
137
  promoteCache?: boolean;
138
138
  }
139
139
 
140
- interface RNMTSocket {
140
+ export interface RNMTSocket {
141
141
  timeout: (timeout?: number) => ({
142
142
  emitWithAck: (...args: any) => Promise<any>;
143
143
  });
@@ -167,13 +167,18 @@ interface CountConfig {
167
167
  disableAuth?: boolean;
168
168
  }
169
169
 
170
- interface FetchHttpResponse extends Response {
170
+ export interface FetchHttpResponse extends Response {
171
171
  /**
172
172
  * true if this response was from local cache
173
173
  */
174
174
  fromCache?: boolean | undefined;
175
175
  }
176
176
 
177
+ interface CustomSocketOption extends OveridenProjectUrl {
178
+ disableAuth?: boolean;
179
+ authHandshake?: Object;
180
+ }
181
+
177
182
  export default class RNMT {
178
183
  constructor(config: RNMTConfig);
179
184
  static initializeCache(option?: ReleaseCacheOption): void;
@@ -183,12 +188,12 @@ export default class RNMT {
183
188
  storage(): RNMTStorage;
184
189
  fetchHttp(endpoint: string, init?: RequestInit, config?: FetchHttpConfig): Promise<FetchHttpResponse>;
185
190
  listenReachableServer(callback: (reachable: boolean) => void): () => void;
186
- getSocket(options: { disableAuth?: boolean; authHandshake?: Object }): RNMTSocket;
191
+ getSocket(options?: CustomSocketOption): RNMTSocket;
187
192
  onConnect: () => CollectionIO;
188
193
  batchWrite(map: BatchWriteValue[], config?: BatchWriteConfig): Promise<DocumentWriteResult[] | undefined>;
189
194
  }
190
195
 
191
- interface RNMTCollection {
196
+ export interface RNMTCollection {
192
197
  find: (find?: DocumentFind) => ({
193
198
  get: (config?: GetConfig) => Promise<DocumentResult[]>;
194
199
  listen: (callback: (snapshot?: DocumentResult[]) => void, onError?: (error?: DocumentError) => void, config?: GetConfig) => void;
@@ -258,7 +263,7 @@ interface RNMTCollection {
258
263
  deleteMany: (find?: DocumentFind, config?: WriteConfig) => Promise<DocumentWriteResult>;
259
264
  }
260
265
 
261
- interface CollectionTaskIO<T> {
266
+ export interface CollectionTaskIO<T> {
262
267
  batchWrite(map: BatchWriteValue[], config?: BatchWriteConfig): T;
263
268
  }
264
269
 
@@ -270,7 +275,7 @@ interface OnConnectChain extends StartStop {
270
275
  onDisconnect: () => CollectionTaskIO<StartStop>;
271
276
  }
272
277
 
273
- interface CollectionIO extends CollectionTaskIO<OnConnectChain> {
278
+ export interface CollectionIO extends CollectionTaskIO<OnConnectChain> {
274
279
  onDisconnect: () => CollectionTaskIO<StartStop>;
275
280
  }
276
281
 
@@ -282,7 +287,7 @@ interface DocumentError extends ErrorResponse {
282
287
 
283
288
  }
284
289
 
285
- interface FetchHttpConfig {
290
+ export interface FetchHttpConfig {
286
291
  retrieval?: GetConfig['retrieval'];
287
292
  /**
288
293
  * disable sending authentication token along with this request
@@ -429,7 +434,7 @@ interface DocumentWriteValue {
429
434
 
430
435
  }
431
436
 
432
- interface RNMTAuth {
437
+ export interface RNMTAuth {
433
438
  customSignin: (email: string, password: string) => Promise<SigninResult>;
434
439
  customSignup: (email: string, password: string, name?: string, metadata?: Object) => Promise<SignupResult>;
435
440
  /**
@@ -465,7 +470,7 @@ export interface SignupResult extends SigninResult {
465
470
  isNewUser: boolean;
466
471
  }
467
472
 
468
- interface AuthData {
473
+ export interface AuthData {
469
474
  email?: string;
470
475
  metadata: Object;
471
476
  signupMethod: auth_provider_id_values;
@@ -473,7 +478,8 @@ interface AuthData {
473
478
  joinedOn: number;
474
479
  uid: string;
475
480
  claims: Object;
476
- emailVerified: boolean;
481
+ authVerified: boolean;
482
+ passwordVerified?: boolean | undefined;
477
483
  tokenID: string;
478
484
  disabled: boolean;
479
485
  entityOf: string;
@@ -487,7 +493,7 @@ interface AuthData {
487
493
  sub: string;
488
494
  }
489
495
 
490
- interface RefreshTokenData {
496
+ export interface RefreshTokenData {
491
497
  uid: string;
492
498
  tokenID: string;
493
499
  isRefreshToken: true;
@@ -497,24 +503,37 @@ interface RefreshTokenData {
497
503
  sub: string;
498
504
  }
499
505
 
500
- interface TokenEventData extends AuthData {
506
+ export interface TokenEventData extends AuthData {
501
507
  tokenManager: TokenManager;
502
508
  }
503
509
 
504
- interface TokenManager {
510
+ export interface TokenManager {
505
511
  refreshToken: string;
506
512
  accessToken: string;
507
513
  }
508
514
 
509
- interface UploadOptions {
515
+ interface OveridenProjectUrl {
516
+ /**
517
+ * The server instance to send requests to. This overrides the parent `projectUrl`, but still relies on the parent `projectUrl` for authentication token resolution and verify server reachability.
518
+ */
519
+ projectUrl?: string;
520
+ }
521
+
522
+ interface UploadOptions extends OveridenProjectUrl {
510
523
  /**
511
524
  * optionally create hash for this upload to save disk space
512
525
  */
513
526
  createHash?: boolean;
527
+
514
528
  /**
515
529
  * wait for a reachable server before initiating the upload task
516
530
  */
517
531
  awaitServer?: boolean;
532
+
533
+ /**
534
+ * monitor upload progress stats
535
+ */
536
+ onProgress?: (stats: UploadProgressStats) => void;
518
537
  }
519
538
 
520
539
  interface DownloadOptions {
@@ -522,16 +541,28 @@ interface DownloadOptions {
522
541
  * wait for a reachable server before initiating the download task
523
542
  */
524
543
  awaitServer?: boolean;
544
+
545
+ /**
546
+ * monitor download progress stats
547
+ */
548
+ onProgress?: (stats: DownloadProgressStats) => void;
549
+ }
550
+
551
+ interface StorageTask extends Promise<string> {
552
+ /**
553
+ * abort the running task process
554
+ */
555
+ abort: () => void;
525
556
  }
526
557
 
527
- interface RNMTStorage {
528
- downloadFile: (link: string, onComplete?: (error?: ErrorResponse, filepath?: string) => void, destination?: string, onProgress?: (stats: DownloadProgressStats) => void, options?: DownloadOptions) => () => void;
529
- uploadFile: (file: string, destination: string, onComplete?: (error?: ErrorResponse, downloadUrl?: string) => void, onProgress?: (stats: UploadProgressStats) => void, options?: UploadOptions) => () => void;
530
- deleteFile: (path: string) => Promise<void>;
531
- deleteFolder: (folder: string) => Promise<void>;
558
+ export interface RNMTStorage {
559
+ downloadFile: (link: string, filepath?: string | undefined, options?: DownloadOptions | undefined) => StorageTask;
560
+ uploadFile: (filepath: string, destination: string, options?: UploadOptions | undefined) => StorageTask;
561
+ deleteFile: (path: string, options?: OveridenProjectUrl | undefined) => Promise<void>;
562
+ deleteFolder: (folder: string, options?: OveridenProjectUrl | undefined) => Promise<void>;
532
563
  }
533
564
 
534
- interface DownloadProgressStats {
565
+ export interface DownloadProgressStats {
535
566
  receivedBtyes: number;
536
567
  expectedBytes: number;
537
568
  isPaused: boolean;
package/src/index.js CHANGED
@@ -165,7 +165,7 @@ class RNMT {
165
165
  listenReachableServer = (callback) => listenReachableServer(callback, this.config.projectUrl);
166
166
 
167
167
  getSocket = (configOpts) => {
168
- const { disableAuth, authHandshake } = configOpts || {};
168
+ const { disableAuth, authHandshake, projectUrl: overidenUrl } = configOpts || {};
169
169
  const { projectUrl, uglify, serverE2E_PublicKey, wsPrefix, extraHeaders } = this.config;
170
170
 
171
171
  const restrictedRoute = [
@@ -278,7 +278,10 @@ class RNMT {
278
278
  const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
279
279
  const [reqBuilder, [privateKey]] = uglify ? await serializeE2E({ a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
280
280
 
281
- socket = io(`${wsPrefix}://${projectUrl.split('://')[1]}`, {
281
+ const getWsPrefix = url => url.startsWith('https') ? 'wss' : 'ws';
282
+ const wsUrl = overidenUrl ? `${getWsPrefix(overidenUrl)}://${overidenUrl.split('://')[1]}` : `${wsPrefix}://${projectUrl.split('://')[1]}`;
283
+
284
+ socket = io(wsUrl, {
282
285
  transports: ['websocket', 'polling', 'flashsocket'],
283
286
  extraHeaders,
284
287
  auth: uglify ? {
@@ -1,4 +1,4 @@
1
- import { doSignOut, revokeAuthIntance } from ".";
1
+ import { doSignOut, revokeAuthIntance } from "./index.js";
2
2
  import EngineApi from "../../helpers/engine_api";
3
3
  import { AuthTokenListener, TokenRefreshListener } from "../../helpers/listeners";
4
4
  import { decodeBinary, deserializeE2E, listenReachableServer } from "../../helpers/peripherals";
@@ -254,11 +254,13 @@ const clearCacheForSignout = (builder, disposeEmulated) => {
254
254
  export const doSignOut = async (builder) => {
255
255
  if (!Scoped.IsStoreReady) await awaitStore();
256
256
  const emulatedURL = CacheStore.EmulatedAuth[builder.projectUrl];
257
+ const thisAuthStore = emulatedURL ? undefined : basicClone(CacheStore.AuthStore[builder.projectUrl]);
257
258
 
258
259
  clearCacheForSignout(builder, !emulatedURL);
259
260
  updateCacheStore(['AuthStore', 'EmulatedAuth']);
260
261
  if (emulatedURL) return;
261
- await revokeAuthIntance(builder);
262
+
263
+ await revokeAuthIntance(builder, thisAuthStore);
262
264
  };
263
265
 
264
266
  export const revokeAuthIntance = async (builder, authStore) => {
@@ -288,8 +290,7 @@ export const purgePendingToken = async (nodeId) => {
288
290
  isConnected = (await (await fetch(_areYouOk(projectUrl), { credentials: 'omit' })).json()).status === 'yes';
289
291
  } catch (_) { }
290
292
 
291
- if (!isConnected)
292
- await awaitReachableServer(projectUrl);
293
+ if (!isConnected) await awaitReachableServer(projectUrl);
293
294
 
294
295
  const [reqBuilder] = await buildFetchInterface({
295
296
  body: { token, r_token },
@@ -3,7 +3,7 @@ import { deserializeE2E, listenReachableServer, niceHash, normalizeRoute, serial
3
3
  import { awaitStore, getReachableServer } from "../../helpers/utils";
4
4
  import { RETRIEVAL } from "../../helpers/values";
5
5
  import { Scoped } from "../../helpers/variables";
6
- import { awaitRefreshToken } from "../auth/accessor";
6
+ import { awaitRefreshToken, parseToken } from "../auth/accessor";
7
7
  import { simplifyCaughtError } from "simplify-error";
8
8
  import { guardObject, Validator } from "guard-object";
9
9
  import { serialize } from "entity-serializer";
@@ -71,17 +71,18 @@ export const mfetch = async (input = '', init, config) => {
71
71
  ) throw `"body" must be any of string, buffer, object`;
72
72
  }
73
73
  await awaitStore();
74
+ const thisToken = disableAuth ? '' : (Scoped.AuthJWTToken[projectUrl] || '');
74
75
 
75
76
  const reqId = await niceHash(
76
77
  serialize([
77
78
  rawHeader,
78
79
  body,
79
- !!disableAuth,
80
+ // !!disableAuth,
80
81
  input,
81
- disableAuth ? '' : (Scoped.AuthJWTToken[projectUrl] || '')
82
+ thisToken && await parseToken(thisToken).uid
82
83
  ]).toString('base64')
83
84
  );
84
- const processReqId = `${reqId}_${disableCache}_${retrieval}`;
85
+ const processReqId = `${reqId}_${thisToken}_${disableCache}_${retrieval}`;
85
86
 
86
87
  let retries = 0, hasFinalize;
87
88
 
@@ -143,7 +144,7 @@ export const mfetch = async (input = '', init, config) => {
143
144
  const [reqBuilder, [privateKey]] = uglified ? await serializeE2E(body, mtoken, serverE2E_PublicKey) : [null, []];
144
145
 
145
146
  const f = await fetch(isLink ? input : `${projectUrl}/${normalizeRoute(input)}`, {
146
- ...(encodeBody || uglified) ? { method: 'POST' } : {},
147
+ ...(hasBody || uglified) ? { method: 'POST' } : {},
147
148
  credentials: 'omit',
148
149
  ...init,
149
150
  ...uglified ? { body: reqBuilder } : encodeBody ? { body: serialize(body) } : {},
@@ -5,6 +5,7 @@ import { DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform } from
5
5
  import { awaitReachableServer, buildFetchInterface, buildFetchResult } from "../../helpers/utils";
6
6
  import { awaitRefreshToken } from "../auth/accessor";
7
7
  import { simplifyError } from "simplify-error";
8
+ import { Validator } from "guard-object";
8
9
 
9
10
  const LINKING_ERROR =
10
11
  `The package 'react-native-mosquito-transport' doesn't seem to be linked. Make sure: \n\n` +
@@ -19,6 +20,7 @@ const RNMTModule = NativeModules.Mosquitodb || (
19
20
  },
20
21
  })
21
22
  );
23
+
22
24
  const emitter = Platform.OS === 'android' ?
23
25
  DeviceEventEmitter : new NativeEventEmitter(RNMTModule);
24
26
 
@@ -27,26 +29,43 @@ export class MTStorage {
27
29
  this.builder = { ...config };
28
30
  }
29
31
 
30
- downloadFile(link = '', onComplete, destination, onProgress, options) {
31
- const { awaitServer } = options || {};
32
+ downloadFile = (link = '', destination, options) => {
33
+ const { awaitServer, onProgress } = options || {};
32
34
  let hasFinished, isPaused, hasCancelled;
33
35
 
34
36
  const { projectUrl, extraHeaders } = this.builder;
37
+ let onComplete;
38
+
39
+ const promise = new Promise((resolve, reject) => {
40
+ onComplete = (err, path) => {
41
+ if (hasFinished) return;
42
+ hasFinished = true;
43
+ if (path) {
44
+ resolve(path);
45
+ } else reject(err);
46
+ }
47
+ });
48
+
49
+ promise.abort = () => {
50
+ if (hasFinished || hasCancelled) return;
51
+ RNMTModule.cancelDownload(processID);
52
+ hasCancelled = true;
53
+ onComplete?.({ error: 'download_aborted', message: 'The download process was aborted' });
54
+ }
35
55
 
36
56
  if (destination && (typeof destination !== 'string' || !destination.trim())) {
37
57
  onComplete?.({ error: 'destination_invalid', message: 'destination must be a non-empty string' });
38
- return () => { };
58
+ return promise;
39
59
  }
40
60
  if (destination) destination = prefixStoragePath(destination?.trim());
41
61
 
42
- if (typeof link !== 'string' || !link.trim().startsWith(`${EngineApi.staticStorage(projectUrl)}/`)) {
62
+ if (typeof link !== 'string' || !Validator.LINK(link = link.trim())) {
43
63
  onComplete?.({
44
64
  error: 'invalid_link',
45
- message: `link has an invalid value, expected a string that starts with "${EngineApi.staticStorage(projectUrl)}/"`
65
+ message: `downloadFile first argument has an invalid value, expected a valid link string but got '${link}' instead`
46
66
  });
47
- return () => { };
67
+ return promise;
48
68
  }
49
- link = link.trim();
50
69
 
51
70
  const processID = `${++Scoped.StorageProcessID}`;
52
71
  const init = async () => {
@@ -86,7 +105,6 @@ export class MTStorage {
86
105
  onComplete?.(path ? undefined : (result?.simpleError || { error, message: errorDes }), path);
87
106
  resultListener.remove();
88
107
  progressListener.remove();
89
- hasFinished = true;
90
108
  });
91
109
 
92
110
  RNMTModule.downloadFile({
@@ -103,30 +121,36 @@ export class MTStorage {
103
121
  }
104
122
 
105
123
  init();
106
-
107
- return () => {
108
- if (hasFinished || hasCancelled) return;
109
- RNMTModule.cancelDownload(processID);
110
- hasCancelled = true;
111
- setTimeout(() => {
112
- onComplete?.({ error: 'download_aborted', message: 'The download process was aborted' });
113
- }, 1);
114
- }
124
+ return promise;
115
125
  }
116
126
 
117
- uploadFile(file = '', destination = '', onComplete, onProgress, options) {
118
- const { createHash, awaitServer } = options || {};
127
+ uploadFile = (file = '', destination = '', options) => {
128
+ const { createHash, awaitServer, onProgress } = options || {};
119
129
  let hasFinished, hasCancelled;
130
+ let thisComplete;
120
131
 
121
- const thisComplete = (...args) => {
122
- if (hasFinished) return;
123
- hasFinished = true;
124
- onComplete?.(...args);
125
- }
132
+ const promise = new Promise((resolve, reject) => {
133
+ thisComplete = (err, url) => {
134
+ if (hasFinished) return;
135
+ hasFinished = true;
136
+ if (url) {
137
+ resolve(url);
138
+ } else reject(err);
139
+ }
140
+ });
141
+
142
+ promise.abort = () => {
143
+ if (hasFinished || hasCancelled) return;
144
+ hasCancelled = true;
145
+ setTimeout(() => {
146
+ thisComplete?.({ error: 'upload_aborted', message: 'The upload process was aborted' });
147
+ }, 0);
148
+ RNMTModule.cancelUpload(processID);
149
+ };
126
150
 
127
151
  if (typeof file !== 'string' || !file.trim()) {
128
152
  thisComplete?.({ error: 'file_path_invalid', message: 'file must be a non-empty string in uploadFile()' });
129
- return () => { };
153
+ return promise;
130
154
  }
131
155
  destination = destination?.trim?.();
132
156
 
@@ -134,7 +158,7 @@ export class MTStorage {
134
158
  validateDestination(destination);
135
159
  } catch (error) {
136
160
  thisComplete?.({ error: 'destination_invalid', message: error });
137
- return () => { };
161
+ return promise;
138
162
  }
139
163
 
140
164
  const isAsset = file.startsWith('ph://') || file.startsWith('content://');
@@ -142,6 +166,7 @@ export class MTStorage {
142
166
 
143
167
  const { projectUrl, uglify, extraHeaders } = this.builder;
144
168
  const processID = `${++Scoped.StorageProcessID}`;
169
+ const thisProjectUrl = options?.projectUrl || projectUrl;
145
170
 
146
171
  const init = async () => {
147
172
  if (awaitServer) await awaitReachableServer(projectUrl);
@@ -167,7 +192,7 @@ export class MTStorage {
167
192
  const authToken = Scoped.AuthJWTToken[projectUrl];
168
193
 
169
194
  RNMTModule.uploadFile({
170
- url: EngineApi._uploadFile(projectUrl, uglify),
195
+ url: EngineApi._uploadFile(thisProjectUrl, uglify),
171
196
  file: isAsset ? file : file.substring('file://'.length),
172
197
  ...authToken ? { authToken } : {},
173
198
  createHash: createHash ? 'yes' : 'no',
@@ -178,24 +203,16 @@ export class MTStorage {
178
203
  }
179
204
 
180
205
  init();
181
-
182
- return () => {
183
- if (hasFinished || hasCancelled) return;
184
- hasCancelled = true;
185
- setTimeout(() => {
186
- thisComplete?.({ error: 'upload_aborted', message: 'The upload process was aborted' });
187
- }, 0);
188
- RNMTModule.cancelUpload(processID);
189
- }
206
+ return promise;
190
207
  }
191
208
 
192
- deleteFile = (path) => deleteContent(this.builder, path);
193
- deleteFolder = (path) => deleteContent(this.builder, path, true);
209
+ deleteFile = (path, options) => deleteContent(this.builder, path, options);
210
+ deleteFolder = (path, options) => deleteContent(this.builder, path, options, true);
194
211
  }
195
212
 
196
213
  const { _deleteFile, _deleteFolder } = EngineApi;
197
214
 
198
- const deleteContent = async (builder, path, isFolder) => {
215
+ const deleteContent = async (builder, path, options, isFolder) => {
199
216
  const { projectUrl, uglify, extraHeaders, serverE2E_PublicKey } = builder;
200
217
 
201
218
  try {
@@ -207,8 +224,10 @@ const deleteContent = async (builder, path, isFolder) => {
207
224
  serverE2E_PublicKey,
208
225
  uglify
209
226
  });
227
+ const thisProjectUrl = options?.projectUrl || projectUrl;
210
228
 
211
- const data = await buildFetchResult(await fetch((isFolder ? _deleteFolder : _deleteFile)(projectUrl, uglify), reqBuilder), uglify);
229
+ const res = await fetch((isFolder ? _deleteFolder : _deleteFile)(thisProjectUrl, uglify), reqBuilder);
230
+ const data = await buildFetchResult(res, uglify);
212
231
  const result = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
213
232
 
214
233
  if (result.status !== 'success') throw 'operation not successful';