react-native-mosquito-transport 0.0.19 → 0.0.21

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.
@@ -1,63 +1,85 @@
1
1
  import { Buffer } from "buffer";
2
- import { cloneInstance, deserializeE2E, listenReachableServer, objToUniqueString, serializeE2E, simplifyCaughtError } from "../../helpers/peripherals";
3
- import { awaitStore, getReachableServer, updateCacheStore, validateRequestMethod } from "../../helpers/utils";
4
- import { RETRIEVAL, Regexs } from "../../helpers/values";
2
+ import { deserializeE2E, listenReachableServer, niceHash, normalizeRoute, serializeE2E } from "../../helpers/peripherals";
3
+ import { awaitStore, getReachableServer, updateCacheStore } from "../../helpers/utils";
4
+ import { RETRIEVAL } from "../../helpers/values";
5
5
  import { CacheStore, Scoped } from "../../helpers/variables";
6
6
  import { awaitRefreshToken } from "../auth/accessor";
7
+ import { simplifyCaughtError } from "simplify-error";
8
+ import { guardObject, Validator } from "guard-object";
9
+ import cloneDeep from "lodash.clonedeep";
10
+ import { serialize } from "entity-serializer";
7
11
 
8
12
  const buildFetchData = (data) => {
9
- const { ok, type, status, statusText, redirected, url, headers, base64, builderCred } = data;
10
- const { uglified, encKey, serverKey } = builderCred;
11
-
12
- const h = {
13
- arrayBuffer: async () => Buffer.from(base64, 'base64'),
14
- json: async () => JSON.parse(await h.text()),
15
- text: async () => {
16
- const txt = Buffer.from(base64, 'base64').toString('utf8');
17
-
18
- if (uglified) {
19
- const json = deserializeE2E(txt, serverKey, encKey);
20
- return `${json}`;
21
- } else return txt;
22
- },
23
- clone: () => ({ ...h }),
24
- type,
13
+ const { ok, type, status, statusText, redirected, url, headers, size, base64 } = data;
14
+
15
+ const response = new Response(Buffer.from(base64, 'base64'), {
16
+ headers: new Headers(headers),
25
17
  status,
26
18
  statusText,
27
- redirected,
28
19
  url,
29
- ok,
30
- headers: new Headers({ ...headers }),
31
- };
32
-
33
- return h;
34
- }
20
+ size
21
+ });
35
22
 
36
- export const mfetch = async (input = '', init = {}, config) => {
37
- const { projectUrl, apiUrl, serverE2E_PublicKey, method, maxRetries = 7, disableCache, accessKey, uglify } = config;
38
- validateRequestMethod(method);
39
-
40
- const { retrieval = RETRIEVAL.DEFAULT, enableMinimizer, rawApproach } = method || {},
41
- isBaseUrl = Regexs.LINK().test(input),
42
- disableAuth = method?.disableAuth || isBaseUrl,
43
- shouldCache = (retrieval === RETRIEVAL.DEFAULT ? !disableCache : true) &&
44
- retrieval !== RETRIEVAL.NO_CACHE_NO_AWAIT,
45
- reqId = objToUniqueString({
46
- ...init,
47
- jij: { disableAuth: !!disableAuth, url: input, projectUrl, retrieval }
23
+ Object.entries({ ok, type, url, redirected, size })
24
+ .forEach(([k, v]) => {
25
+ if (response[k] !== v)
26
+ Object.defineProperty(response, k, {
27
+ value: v,
28
+ writable: false
29
+ });
48
30
  });
49
31
 
50
- if ('mtoken' in (init?.headers || {}))
32
+ return response;
33
+ }
34
+
35
+ export const mfetch = async (input = '', init, config) => {
36
+ const { projectUrl, serverE2E_PublicKey, method, maxRetries = 7, disableCache, accessKey, uglify, extraHeaders } = config;
37
+ const { headers, body } = init || {};
38
+
39
+ if (method !== undefined)
40
+ guardObject({
41
+ enableMinimizer: t => t === undefined || Validator.BOOLEAN(t),
42
+ rawApproach: t => t === undefined || Validator.BOOLEAN(t),
43
+ disableAuth: t => t === undefined || Validator.BOOLEAN(t),
44
+ retrieval: t => t === undefined || Object.values(RETRIEVAL).includes(t)
45
+ }).validate(method);
46
+
47
+ const { retrieval = RETRIEVAL.DEFAULT, enableMinimizer, rawApproach } = method || {};
48
+ const isBaseUrl = Validator.LINK(input);
49
+ const disableAuth = method?.disableAuth || isBaseUrl;
50
+ const shouldCache = (retrieval !== RETRIEVAL.DEFAULT || !disableCache) &&
51
+ retrieval !== RETRIEVAL.NO_CACHE_NO_AWAIT;
52
+ const uglified = !!(!isBaseUrl && uglify);
53
+
54
+ const rawHeader = Object.fromEntries(
55
+ [...new Headers(headers).entries()]
56
+ );
57
+
58
+ if ('mtoken' in rawHeader)
51
59
  throw '"mtoken" in header is a reserved prop';
52
60
 
53
- if ('uglified' in (init?.headers || {}))
61
+ if ('uglified' in rawHeader)
54
62
  throw '"uglified" in header is a reserved prop';
55
63
 
56
- if (input.startsWith(projectUrl) && !rawApproach)
57
- throw `fetchHttp first argument can not starts with projectUrl:"${projectUrl}", please set {rawApproach: true} if you're trying to access this url as it is`;
58
-
59
- if (!isBaseUrl && init.body && typeof init.body !== 'string')
60
- throw `"body" must be a string value`;
64
+ // if (isBaseUrl && !rawApproach)
65
+ // throw `please set { rawApproach: true } if you're trying to access different endpoint at "${input}"`;
66
+
67
+ if (body !== undefined) {
68
+ if (
69
+ typeof body !== 'string' &&
70
+ !Buffer.isBuffer(body) &&
71
+ !Validator.JSON(body)
72
+ ) throw `"body" must be any of string, buffer, object`;
73
+ }
74
+
75
+ const reqId = await niceHash(
76
+ serialize([
77
+ rawHeader,
78
+ body,
79
+ !!disableAuth,
80
+ input
81
+ ]).toString('base64')
82
+ );
61
83
 
62
84
  let retries = 0, hasFinalize;
63
85
 
@@ -72,7 +94,7 @@ export const mfetch = async (input = '', init = {}, config) => {
72
94
 
73
95
  if (enableMinimizer) {
74
96
  (Scoped.PendingFetchCollective.pendingResolution[reqId] || []).forEach(e => {
75
- e(cloneInstance(a), b);
97
+ e(a, b);
76
98
  });
77
99
  if (Scoped.PendingFetchCollective.pendingResolution[reqId])
78
100
  delete Scoped.PendingFetchCollective.pendingResolution[reqId];
@@ -83,16 +105,15 @@ export const mfetch = async (input = '', init = {}, config) => {
83
105
  };
84
106
 
85
107
  await awaitStore();
86
- const reqData = CacheStore.FetchedStore[reqId],
87
- resolveCache = () => {
88
- finalize({
89
- ...buildFetchData(reqData),
90
- fromCache: true
91
- });
92
- };
108
+ const reqData = CacheStore.FetchedStore[projectUrl]?.[reqId];
109
+ const resolveCache = () => {
110
+ finalize({
111
+ ...buildFetchData(reqData),
112
+ fromCache: true
113
+ });
114
+ };
93
115
 
94
116
  try {
95
-
96
117
  if (retryProcess === 1) {
97
118
  if (enableMinimizer) {
98
119
  if (Scoped.PendingFetchCollective.pendingProcess[reqId]) {
@@ -100,8 +121,8 @@ export const mfetch = async (input = '', init = {}, config) => {
100
121
  Scoped.PendingFetchCollective.pendingResolution[reqId] = [];
101
122
 
102
123
  Scoped.PendingFetchCollective.pendingResolution[reqId].push((a, b) => {
103
- if (a) resolve(a.result);
104
- else reject(b);
124
+ if (a) resolve(cloneDeep(a.result));
125
+ else reject(cloneDeep(b));
105
126
  });
106
127
  return;
107
128
  }
@@ -117,53 +138,56 @@ export const mfetch = async (input = '', init = {}, config) => {
117
138
  if (!disableAuth && await getReachableServer(projectUrl))
118
139
  await awaitRefreshToken(projectUrl);
119
140
 
120
- const mtoken = Scoped.AuthJWTToken[projectUrl],
121
- uglified = (!isBaseUrl && init?.body && typeof init?.body === 'string' && uglify),
122
- initType = extractHeaderItem('content-type', init?.headers);
141
+ const mtoken = Scoped.AuthJWTToken[projectUrl];
142
+ const initType = rawHeader['content-type'];
123
143
 
124
- const [reqBuilder, [privateKey]] = (uglified && isBaseUrl) ? serializeE2E(init.body, mtoken, serverE2E_PublicKey) : [null, []];
144
+ const [reqBuilder, [privateKey]] = uglified ? await serializeE2E(body, mtoken, serverE2E_PublicKey) : [null, []];
125
145
 
126
- const f = await fetch(isBaseUrl ? input : `${apiUrl}/${input}`, {
146
+ const f = await fetch(isBaseUrl ? input : `${projectUrl}/${normalizeRoute(input)}`, {
127
147
  ...isBaseUrl ? {} : { method: 'POST' },
128
148
  ...init,
129
149
  ...uglified ? { body: reqBuilder } : {},
130
150
  cache: 'no-cache',
131
151
  headers: {
132
- ...isBaseUrl ? {} : { 'Content-type': 'application/json' },
133
- ...init?.headers,
152
+ ...extraHeaders,
153
+ ...isBaseUrl ? {} : { 'content-type': 'application/json' },
154
+ ...rawHeader,
134
155
  ...uglified ? {
135
156
  uglified,
136
- 'Content-type': 'text/plain',
157
+ 'content-type': 'request/buffer',
137
158
  ...initType ? { 'init-content-type': initType } : {}
138
159
  } : {},
139
- ...((disableAuth || !mtoken || uglified || isBaseUrl) ? {} : { mtoken }),
160
+ ...(disableAuth || !mtoken || uglified || isBaseUrl) ? {} : { mtoken },
140
161
  ...isBaseUrl ? {} : { authorization: `Bearer ${accessKey}` }
141
162
  }
142
- }),
143
- { ok, type, status, statusText, redirected, url, headers } = f,
144
- simple = headers.get('simple_error');
163
+ });
164
+ const { ok, type, status, statusText, redirected, url, headers, size } = f;
165
+ const simple = headers.get('simple_error');
145
166
 
146
167
  if (!isBaseUrl && simple) throw { simpleError: JSON.parse(simple) };
147
168
 
148
- const base64 = Buffer.from(await f.arrayBuffer()).toString('base64'),
149
- resObj = {
150
- builderCred: {
151
- uglified,
152
- encKey: privateKey,
153
- serverKey: serverE2E_PublicKey
154
- },
155
- base64,
156
- type,
157
- status,
158
- statusText,
159
- redirected,
160
- url,
161
- ok,
162
- headers: headerObj(headers)
163
- };
169
+ const base64 = uglified ?
170
+ Buffer.from(await deserializeE2E(await f.arrayBuffer(), serverE2E_PublicKey, privateKey)).toString('base64') :
171
+ Buffer.from(await f.arrayBuffer()).toString('base64');
172
+
173
+ const resObj = {
174
+ base64,
175
+ type,
176
+ status,
177
+ statusText,
178
+ redirected,
179
+ url,
180
+ ok,
181
+ size,
182
+ headers: Object.fromEntries(
183
+ [...headers.entries()]
184
+ )
185
+ };
164
186
 
165
187
  if (shouldCache) {
166
- CacheStore.FetchedStore[reqId] = { ...resObj };
188
+ if (!CacheStore.FetchedStore[projectUrl])
189
+ CacheStore.FetchedStore[projectUrl] = {};
190
+ CacheStore.FetchedStore[projectUrl][reqId] = cloneDeep(resObj);
167
191
  updateCacheStore();
168
192
  }
169
193
 
@@ -207,24 +231,5 @@ export const mfetch = async (input = '', init = {}, config) => {
207
231
  }
208
232
  });
209
233
 
210
- const r = await callFetch();
211
- return r;
212
- };
213
-
214
- const extractHeaderItem = (t = '', header) => {
215
- let k;
216
- Object.entries(header || {}).forEach(([key, value]) => {
217
- if (key.toLowerCase() === t.toLowerCase()) k = value;
218
- });
219
- return k;
220
- }
221
-
222
- const headerObj = (header) => {
223
- const h = {};
224
-
225
- header.forEach((v, k) => {
226
- h[k] = v;
227
- });
228
-
229
- return h;
230
- }
234
+ return await callFetch();
235
+ };
@@ -1,9 +1,10 @@
1
- import EngineApi from "../../helpers/EngineApi";
1
+ import EngineApi from "../../helpers/engine_api";
2
2
  import { encodeBinary, prefixStoragePath } from "../../helpers/peripherals";
3
3
  import { Scoped } from "../../helpers/variables";
4
4
  import { DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform } from 'react-native';
5
- import { awaitReachableServer, buildFetchInterface, simplifyError } from "../../helpers/utils";
5
+ import { awaitReachableServer, buildFetchInterface } from "../../helpers/utils";
6
6
  import { awaitRefreshToken } from "../auth/accessor";
7
+ import { simplifyError } from "simplify-error";
7
8
 
8
9
  const LINKING_ERROR =
9
10
  `The package 'react-native-mosquito-transport' doesn't seem to be linked. Make sure: \n\n` +
@@ -17,22 +18,23 @@ const RNMTModule = NativeModules.Mosquitodb || (
17
18
  throw new Error(LINKING_ERROR);
18
19
  },
19
20
  })
20
- ),
21
- emitter = Platform.OS === 'android' ?
22
- DeviceEventEmitter : new NativeEventEmitter(RNMTModule);
21
+ );
22
+ const emitter = Platform.OS === 'android' ?
23
+ DeviceEventEmitter : new NativeEventEmitter(RNMTModule);
23
24
 
24
25
  export class MTStorage {
25
26
  constructor(config) {
26
27
  this.builder = { ...config };
27
28
  }
28
29
 
29
- downloadFile(link = '', onComplete, destination, onProgress) {
30
+ downloadFile(link = '', onComplete, destination, onProgress, options) {
31
+ const { awaitServer } = options || {};
30
32
  let hasFinished, isPaused, hasCancelled;
31
33
 
32
- const { projectUrl, accessKey, awaitStorage } = this.builder;
34
+ const { projectUrl, accessKey, extraHeaders } = this.builder;
33
35
 
34
- if (destination && (!destination?.trim() || typeof destination !== 'string')) {
35
- onComplete?.({ error: 'destination_invalid', message: 'destination is invalid in downloadFile()' });
36
+ if (destination && (typeof destination !== 'string' || !destination.trim())) {
37
+ onComplete?.({ error: 'destination_invalid', message: 'destination must be a non-empty string' });
36
38
  return () => { };
37
39
  }
38
40
  if (destination) destination = prefixStoragePath(destination?.trim());
@@ -46,57 +48,58 @@ export class MTStorage {
46
48
  }
47
49
  link = link.trim();
48
50
 
51
+ const processID = `${++Scoped.StorageProcessID}`;
49
52
  const init = async () => {
50
- if (awaitStorage) await awaitReachableServer(projectUrl);
53
+ if (awaitServer) await awaitReachableServer(projectUrl);
51
54
  await awaitRefreshToken(projectUrl);
52
55
 
53
56
  if (hasCancelled) return;
54
57
 
55
- const processID = `${++Scoped.StorageProcessID}`,
56
- progressListener = emitter.addListener('mt-download-progress', ({ processID: ref, receivedBtyes, expectedBytes }) => {
57
- if (processID !== ref || hasFinished || hasCancelled) return;
58
- onProgress?.({
59
- receivedBtyes,
60
- expectedBytes,
61
- isPaused: !!isPaused,
62
- pause: () => {
63
- if (hasFinished || isPaused || hasCancelled) return;
64
- RNMTModule.pauseDownload(processID);
65
- isPaused = true;
66
- },
67
- resume: () => {
68
- if (hasFinished || !isPaused || hasCancelled) return;
69
- RNMTModule.pauseDownload(processID);
70
- isPaused = false;
71
- }
72
- });
73
- }),
74
- resultListener = emitter.addListener('mt-download-status', ({ processID: ref, error, errorDes, result }) => {
75
- if (processID !== ref) return;
76
- if (result)
77
- try {
78
- result = JSON.parse(result);
79
- } catch (e) { }
80
-
81
- const path = result?.file || undefined;
82
-
83
- if (!hasFinished && !hasCancelled)
84
- onComplete?.(path ? undefined : (result?.simpleError || { error, errorDes }), path);
85
- resultListener.remove();
86
- progressListener.remove();
87
- hasFinished = true;
58
+ const progressListener = emitter.addListener('mt-download-progress', ({ processID: ref, receivedBtyes, expectedBytes }) => {
59
+ if (processID !== ref || hasFinished || hasCancelled) return;
60
+ onProgress?.({
61
+ receivedBtyes,
62
+ expectedBytes,
63
+ isPaused: !!isPaused,
64
+ pause: () => {
65
+ if (hasFinished || isPaused || hasCancelled) return;
66
+ RNMTModule.pauseDownload(processID);
67
+ isPaused = true;
68
+ },
69
+ resume: () => {
70
+ if (hasFinished || !isPaused || hasCancelled) return;
71
+ RNMTModule.resumeDownload(processID);
72
+ isPaused = false;
73
+ }
88
74
  });
75
+ });
76
+ const resultListener = emitter.addListener('mt-download-status', ({ processID: ref, error, errorDes, result }) => {
77
+ if (processID !== ref) return;
78
+ if (result)
79
+ try {
80
+ result = JSON.parse(result);
81
+ } catch (e) { }
82
+
83
+ const path = result?.file || undefined;
84
+
85
+ if (!hasFinished && !hasCancelled)
86
+ onComplete?.(path ? undefined : (result?.simpleError || { error, message: errorDes }), path);
87
+ resultListener.remove();
88
+ progressListener.remove();
89
+ hasFinished = true;
90
+ });
89
91
 
90
92
  RNMTModule.downloadFile({
91
93
  url: link,
92
94
  authToken: Scoped.AuthJWTToken[projectUrl],
93
- ...(destination ? {
95
+ ...destination ? {
94
96
  destination: destination.substring('file://'.length),
95
- destinationDir: `${destination.substring('file://'.length)}`.split('/').filter((_, i, a) => i !== a.length - 1).join('/')
96
- } : {}),
97
+ destinationDir: `${destination.substring('file://'.length)}`.split('/').slice(0, -1).join('/')
98
+ } : {},
97
99
  processID,
98
100
  urlName: link.split('/').pop(),
99
- authorization: `Bearer ${encodeBinary(accessKey)}`
101
+ authorization: `Bearer ${encodeBinary(accessKey)}`,
102
+ extraHeaders: extraHeaders || {},
100
103
  });
101
104
  }
102
105
 
@@ -112,60 +115,64 @@ export class MTStorage {
112
115
  }
113
116
  }
114
117
 
115
- uploadFile(file = '', destination = '', onComplete, onProgress) {
118
+ uploadFile(file = '', destination = '', onComplete, onProgress, options) {
119
+ const { createHash, awaitServer } = options || {};
116
120
  let hasFinished, hasCancelled;
117
121
 
118
- if (!file?.trim() || typeof file !== 'string') {
122
+ if (typeof file !== 'string' || !file.trim()) {
119
123
  onComplete?.({ error: 'file_path_invalid', message: 'file must be a non-empty string in uploadFile()' });
120
124
  return () => { };
121
125
  }
122
- destination = destination?.trim();
123
-
124
- const destErr = validateDestination(destination),
125
- isAsset = (file.startsWith('ph://') || file.startsWith('content://'));
126
+ destination = destination?.trim?.();
126
127
 
127
- file = isAsset ? file.trim() : prefixStoragePath(file.trim());
128
-
129
- if (destErr) {
130
- onComplete?.({ error: 'destination_invalid', message: destErr });
128
+ try {
129
+ validateDestination(destination);
130
+ } catch (error) {
131
+ onComplete?.({ error: 'destination_invalid', message: error });
131
132
  return () => { };
132
133
  }
133
134
 
134
- const { projectUrl, accessKey, awaitStorage } = this.builder,
135
- processID = `${++Scoped.StorageProcessID}`;
135
+ const isAsset = (file.startsWith('ph://') || file.startsWith('content://'));
136
+
137
+ file = isAsset ? file.trim() : prefixStoragePath(file.trim());
138
+
139
+ const { projectUrl, accessKey, uglify, extraHeaders } = this.builder;
140
+ const processID = `${++Scoped.StorageProcessID}`;
136
141
 
137
142
  const init = async () => {
138
- if (awaitStorage) await awaitReachableServer(projectUrl);
143
+ if (awaitServer) await awaitReachableServer(projectUrl);
139
144
  await awaitRefreshToken(projectUrl);
140
145
 
141
146
  if (hasCancelled) return;
142
147
  const progressListener = emitter.addListener('mt-uploading-progress', ({ processID: ref, sentBtyes, totalBytes }) => {
143
148
  if (processID !== ref || hasFinished || hasCancelled) return;
144
149
  onProgress?.({ sentBtyes, totalBytes });
145
- }),
146
- resultListener = emitter.addListener('mt-uploading-status', ({ processID: ref, error, errorDes, result }) => {
147
- if (processID !== ref || hasFinished) return;
148
- if (result)
149
- try {
150
- result = JSON.parse(result);
151
- } catch (e) { }
152
-
153
- const downloadUrl = result?.downloadUrl || undefined;
154
-
155
- if (!hasFinished && !hasCancelled)
156
- onComplete?.(downloadUrl ? undefined : (result?.simpleError || { error, errorDes }), downloadUrl);
157
- resultListener.remove();
158
- progressListener.remove();
159
- hasFinished = true;
160
- });
150
+ });
151
+ const resultListener = emitter.addListener('mt-uploading-status', ({ processID: ref, error, errorDes, result }) => {
152
+ if (processID !== ref || hasFinished) return;
153
+ if (result)
154
+ try {
155
+ result = JSON.parse(result);
156
+ } catch (e) { }
157
+
158
+ const downloadUrl = result?.downloadUrl || undefined;
159
+
160
+ if (!hasFinished && !hasCancelled)
161
+ onComplete?.(downloadUrl ? undefined : (result?.simpleError || { error, message: errorDes }), downloadUrl);
162
+ resultListener.remove();
163
+ progressListener.remove();
164
+ hasFinished = true;
165
+ });
161
166
 
162
167
  RNMTModule.uploadFile({
163
- url: EngineApi._uploadFile(projectUrl),
168
+ url: EngineApi._uploadFile(projectUrl, uglify),
164
169
  file: isAsset ? file : file.substring('file://'.length),
165
170
  authToken: Scoped.AuthJWTToken[projectUrl],
171
+ createHash: createHash ? 'yes' : 'no',
166
172
  destination,
167
173
  processID,
168
- authorization: `Bearer ${encodeBinary(accessKey)}`
174
+ authorization: `Bearer ${encodeBinary(accessKey)}`,
175
+ extraHeaders: extraHeaders || {}
169
176
  });
170
177
  }
171
178
 
@@ -186,12 +193,12 @@ export class MTStorage {
186
193
  }
187
194
 
188
195
  const deleteContent = async (builder, path, isFolder) => {
189
- const { projectUrl, accessKey } = builder;
196
+ const { projectUrl, accessKey, uglify } = builder;
190
197
 
191
198
  try {
192
199
  const r = await (await fetch(
193
- EngineApi[isFolder ? '_deleteFolder' : '_deleteFile'](projectUrl),
194
- buildFetchInterface({ path }, accessKey, Scoped.AuthJWTToken[projectUrl], 'DELETE')
200
+ EngineApi[isFolder ? '_deleteFolder' : '_deleteFile'](projectUrl, uglify),
201
+ await buildFetchInterface({ path }, accessKey, Scoped.AuthJWTToken[projectUrl], 'DELETE')
195
202
  )).json();
196
203
  if (r.simpleError) throw r;
197
204
  if (r.status !== 'success') throw 'operation not successful';
@@ -202,16 +209,18 @@ const deleteContent = async (builder, path, isFolder) => {
202
209
  }
203
210
 
204
211
  const validateDestination = (t = '') => {
205
- t = t.trim();
212
+ if (typeof t !== 'string' || !t.trim()) throw 'path must be a non-empty string';
213
+ if (t.startsWith(' ') || t.endsWith(' ')) throw 'path must be trimmed';
214
+ if (t.startsWith('./') || t.startsWith('../')) throw 'path must be absolute';
215
+ if (t.endsWith('/')) throw 'path must not end with "/"';
216
+ if ('?'.split('').some(v => t.includes(v)))
217
+ throw `path must not contain ?`;
206
218
 
207
- if (!t || typeof t !== 'string') return `destination is required`;
208
- if (t.startsWith('/') || t.endsWith('/')) return 'destination must neither start with "/" nor end with "/"';
209
- let l = '', r;
219
+ t = t.trim();
220
+ let l = '';
210
221
 
211
222
  t.split('').forEach(e => {
212
- if (e === '/' && l === '/') r = 'invalid destination path, "/" cannot be side by side';
223
+ if (e === '/' && l === '/') throw 'invalid destination path, "/" cannot be duplicated side by side';
213
224
  l = e;
214
225
  });
215
-
216
- return r;
217
226
  };
@@ -1,33 +0,0 @@
1
- import { encodeBinary } from './peripherals';
2
-
3
- const apis = {
4
- _readDocument: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._readDocument(baseApi)) : '_readDocument'}`,
5
- _writeDocument: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._writeDocument(baseApi)) : '_writeDocument'}`,
6
- _deleteCollection: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._deleteCollection(baseApi)) : '_deleteCollection'}`,
7
- _queryCollection: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._queryCollection(baseApi)) : '_queryCollection'}`,
8
- _writeMapDocument: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._writeMapDocument(baseApi)) : '_writeMapDocument'}`,
9
- _customSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._customSignin(baseApi)) : '_customSignin'}`,
10
- _customSignup: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._customSignup(baseApi)) : '_customSignup'}`,
11
- _googleSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._googleSignin(baseApi)) : '_googleSignin'}`,
12
- _appleSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._appleSignin(baseApi)) : '_appleSignin'}`,
13
- _facebookSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._facebookSignin(baseApi)) : '_facebookSignin'}`,
14
- _twitterSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._twitterSignin(baseApi)) : '_twitterSignin'}`,
15
- _githubSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._githubSignin(baseApi)) : '_githubSignin'}`,
16
- _signOut: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._signOut(baseApi)) : '_signOut'}`,
17
- _refreshAuthToken: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._refreshAuthToken(baseApi)) : '_refreshAuthToken'}`,
18
- _downloadFile: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._downloadFile(baseApi)) : '_downloadFile'}`,
19
- _uploadFile: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._uploadFile(baseApi)) : '_uploadFile'}`,
20
- _deleteFile: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._deleteFile(baseApi)) : '_deleteFile'}`,
21
- _deleteFolder: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._deleteFolder(baseApi)) : '_deleteFolder'}`,
22
- staticStorage: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis.staticStorage(baseApi)) : 'storage'}`,
23
- _documentCount: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._documentCount(baseApi)) : '_documentCount'}`,
24
- _areYouOk: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._areYouOk(baseApi)) : '_areYouOk'}`,
25
- // static path
26
- _listenCollection: (ugly) => `${ugly ? encodeBinary(apis._listenCollection()) : '_listenCollection'}`,
27
- _listenDocument: (ugly) => `${ugly ? encodeBinary(apis._listenDocument()) : '_listenDocument'}`,
28
- _startDisconnectWriteTask: (ugly) => `${ugly ? encodeBinary(apis._startDisconnectWriteTask()) : '_startDisconnectWriteTask'}`,
29
- _cancelDisconnectWriteTask: (ugly) => `${ugly ? encodeBinary(apis._cancelDisconnectWriteTask()) : '_cancelDisconnectWriteTask'}`,
30
- _listenUserVerification: (ugly) => `${ugly ? encodeBinary(apis._listenUserVerification()) : '_listenUserVerification'}`
31
- };
32
-
33
- export default { ...apis };