react-native-mosquito-transport 0.0.14
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/CODE_OF_CONDUCT.md +133 -0
- package/CONTRIBUTING.md +114 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/android/build.gradle +77 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/mosquitodb/MosquitodbModule.java +32 -0
- package/android/src/main/java/com/mosquitodb/MosquitodbPackage.java +28 -0
- package/example/.bundle/config +2 -0
- package/example/.node-version +1 -0
- package/example/.watchmanconfig +1 -0
- package/example/Gemfile +6 -0
- package/example/android/app/build.gradle +170 -0
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +10 -0
- package/example/android/app/src/debug/AndroidManifest.xml +13 -0
- package/example/android/app/src/debug/java/com/mosquitodbexample/ReactNativeFlipper.java +75 -0
- package/example/android/app/src/main/AndroidManifest.xml +25 -0
- package/example/android/app/src/main/java/com/mosquitodbexample/MainActivity.java +35 -0
- package/example/android/app/src/main/java/com/mosquitodbexample/MainApplication.java +62 -0
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +3 -0
- package/example/android/app/src/main/res/values/styles.xml +9 -0
- package/example/android/app/src/release/java/com/mosquitodbexample/ReactNativeFlipper.java +20 -0
- package/example/android/build.gradle +21 -0
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/example/android/gradle.properties +44 -0
- package/example/android/gradlew +234 -0
- package/example/android/gradlew.bat +89 -0
- package/example/android/settings.gradle +4 -0
- package/example/app.json +4 -0
- package/example/babel.config.js +17 -0
- package/example/index.js +5 -0
- package/example/ios/.xcode.env +11 -0
- package/example/ios/File.swift +6 -0
- package/example/ios/MosquitodbExample/AppDelegate.h +6 -0
- package/example/ios/MosquitodbExample/AppDelegate.mm +36 -0
- package/example/ios/MosquitodbExample/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/example/ios/MosquitodbExample/Images.xcassets/Contents.json +6 -0
- package/example/ios/MosquitodbExample/Info.plist +55 -0
- package/example/ios/MosquitodbExample/LaunchScreen.storyboard +47 -0
- package/example/ios/MosquitodbExample/main.m +10 -0
- package/example/ios/MosquitodbExample-Bridging-Header.h +3 -0
- package/example/ios/MosquitodbExample.xcodeproj/project.pbxproj +702 -0
- package/example/ios/MosquitodbExample.xcodeproj/xcshareddata/xcschemes/MosquitodbExample.xcscheme +88 -0
- package/example/ios/MosquitodbExampleTests/Info.plist +24 -0
- package/example/ios/MosquitodbExampleTests/MosquitodbExampleTests.m +66 -0
- package/example/ios/Podfile +60 -0
- package/example/metro.config.js +40 -0
- package/example/package.json +22 -0
- package/example/react-native.config.js +10 -0
- package/example/src/App.tsx +31 -0
- package/ios/Mosquitodb-Bridging-Header.h +2 -0
- package/ios/Mosquitodb.m +22 -0
- package/ios/Mosquitodb.swift +305 -0
- package/ios/Mosquitodb.xcodeproj/project.pbxproj +283 -0
- package/package.json +45 -0
- package/react-native-mosquitodb.podspec +35 -0
- package/src/helpers/EngineApi.js +34 -0
- package/src/helpers/listeners.js +7 -0
- package/src/helpers/peripherals.js +195 -0
- package/src/helpers/utils.js +113 -0
- package/src/helpers/values.js +72 -0
- package/src/helpers/variables.js +34 -0
- package/src/index.d.ts +373 -0
- package/src/index.js +369 -0
- package/src/products/auth/accessor.js +151 -0
- package/src/products/auth/index.js +279 -0
- package/src/products/database/accessor.js +316 -0
- package/src/products/database/index.js +603 -0
- package/src/products/database/types.js +22 -0
- package/src/products/database/validator.js +282 -0
- package/src/products/http_callable/index.js +230 -0
- package/src/products/storage/index.js +217 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { IS_DECIMAL_NUMBER, IS_RAW_OBJECT, IS_WHOLE_NUMBER, queryEntries } from "../../helpers/peripherals";
|
|
2
|
+
import { READ_OPS, READ_OPS_LIST, RETRIEVAL } from "../../helpers/values";
|
|
3
|
+
import getLodash from 'lodash/get';
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
|
|
6
|
+
const dirn = ['desc', 'asc', 'ascending', 'descending'];
|
|
7
|
+
|
|
8
|
+
export const validateReadConfig = (config, excludedNodes = []) => {
|
|
9
|
+
const nodeList = [
|
|
10
|
+
'excludeFields',
|
|
11
|
+
'returnOnly',
|
|
12
|
+
'extraction',
|
|
13
|
+
'episode',
|
|
14
|
+
'retrieval',
|
|
15
|
+
'disableAuth',
|
|
16
|
+
'disableMinimizer'
|
|
17
|
+
].filter(v => !excludedNodes.includes(v));
|
|
18
|
+
|
|
19
|
+
if (config) {
|
|
20
|
+
if (!IS_RAW_OBJECT(config)) throw `Invalid value assigned to 'config', expected a raw object`;
|
|
21
|
+
Object.entries(config).forEach(([k, v]) => {
|
|
22
|
+
if (!nodeList.includes(k)) throw `unexpected property '${k}' found in config`;
|
|
23
|
+
|
|
24
|
+
if (k === 'excludeFields' || k === 'returnOnly') {
|
|
25
|
+
if (typeof v !== 'string' && !Array.isArray(v))
|
|
26
|
+
throw `invalid value supplied to ${k}, expected either a string or array of string`;
|
|
27
|
+
if (Array.isArray(v)) {
|
|
28
|
+
v.forEach(e => {
|
|
29
|
+
if (typeof e !== 'string')
|
|
30
|
+
throw `invalid value supplied to ${k}, expected a string in array but got ${e}`;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
} else if (k === 'extraction') {
|
|
34
|
+
((Array.isArray(v) ? v : [v]).forEach((e, i) => {
|
|
35
|
+
const { limit, sort, direction, collection, find, findOne } = e;
|
|
36
|
+
if (typeof limit === 'number' && (!IS_WHOLE_NUMBER(limit) || limit <= 0))
|
|
37
|
+
throw `invalid value supplied to limit of extraction[${i}], expected a positive whole number but got ${limit}`;
|
|
38
|
+
|
|
39
|
+
if (sort && typeof sort !== 'string')
|
|
40
|
+
throw `invalid value supplied to sort in extraction[${i}], expected a string value but got ${sort}`;
|
|
41
|
+
|
|
42
|
+
if (collection && typeof collection !== 'string')
|
|
43
|
+
throw `invalid value supplied to collection in extraction[${i}], expected a string value but got ${collection}`;
|
|
44
|
+
|
|
45
|
+
if (direction && direction !== 1 && direction !== -1 && !dirn.includes(direction))
|
|
46
|
+
throw `invalid value supplied to direction in extraction[${i}], expected any of ${[1, -1, ...dirn]} but got ${direction}`;
|
|
47
|
+
}));
|
|
48
|
+
} else if (k === 'episode') {
|
|
49
|
+
if (v !== 0 && v !== 1) throw `invalid value supplied to ${k}, expected either 0 or 1 but got ${v}`;
|
|
50
|
+
} else if (k === 'retrieval') {
|
|
51
|
+
const h = Object.values(RETRIEVAL);
|
|
52
|
+
if (!h.includes(v))
|
|
53
|
+
throw `invalid value supplied to ${k}, expected any of ${h} but got ${v}`;
|
|
54
|
+
} else if (k === 'disableAuth' || k === 'disableMinimizer') {
|
|
55
|
+
if (typeof v !== 'boolean')
|
|
56
|
+
throw `invalid value supplied to ${k}, expected a boolean value but got ${v}`;
|
|
57
|
+
} else throw `unexpected property '${k}' found in config`;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const validateWriteValue = (value, filter, type) => {
|
|
63
|
+
const isObject = IS_RAW_OBJECT(value);
|
|
64
|
+
|
|
65
|
+
if (type === 'setOne' || type === 'setMany') {
|
|
66
|
+
if (type === 'setOne' && !isObject) {
|
|
67
|
+
throw `expected raw object in ${type}() operation but got ${value}`;
|
|
68
|
+
} else if (type === 'setMany' && !Array.isArray(value))
|
|
69
|
+
throw `expected an array of document in ${type}() operation but got ${value}`;
|
|
70
|
+
|
|
71
|
+
const duplicateID = {};
|
|
72
|
+
|
|
73
|
+
(Array.isArray(value) ? value : [value]).forEach(e => {
|
|
74
|
+
if (!IS_RAW_OBJECT(e)) throw `expected raw object in ${type}() operation but got ${e}`;
|
|
75
|
+
if (duplicateID[e._id]) throw `duplicate document _id:${e._id} found in ${type}() operation`;
|
|
76
|
+
if (!e._id) throw `No _id found in ${type}() operation`;
|
|
77
|
+
duplicateID[e._id] = true;
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (type !== 'deleteOne' && type !== 'deleteMany')
|
|
83
|
+
if (!isObject) throw `expected raw object in ${type}() operation but got ${value}`;
|
|
84
|
+
|
|
85
|
+
validateFilter(filter);
|
|
86
|
+
|
|
87
|
+
queryEntries(value, []).forEach(([segment]) => {
|
|
88
|
+
if (segment.filter(v => v === '_foreign_doc').length)
|
|
89
|
+
throw `"_foreign_doc" is a reserved word, don't use it as a field in a document`;
|
|
90
|
+
|
|
91
|
+
// TODO: validate rest
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const validateFilter = (filter = {}) => evaluateFilter({}, filter);
|
|
96
|
+
|
|
97
|
+
export const validateCollectionPath = (path) => {
|
|
98
|
+
if (typeof path !== 'string' || path.includes('.') || !path.trim())
|
|
99
|
+
throw `invalid collection path "${path}", expected non-empty string and mustn't contain "."`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const confirmFilterDoc = (data, filter) => {
|
|
103
|
+
// [$and, $or]
|
|
104
|
+
const logics = [[], []];
|
|
105
|
+
|
|
106
|
+
Object.entries(filter).forEach(([key, value]) => {
|
|
107
|
+
if (key === '$and') {
|
|
108
|
+
if (!Array.isArray(value)) throw `$and must be an array`;
|
|
109
|
+
value.forEach(v => logics[0].push(evaluateFilter(data, v)));
|
|
110
|
+
} else if (key === '$or') {
|
|
111
|
+
if (!Array.isArray(value)) throw `$and must be an array`;
|
|
112
|
+
value.forEach(v => logics[1].push(evaluateFilter(data, v)));
|
|
113
|
+
} else logics[0].push(evaluateFilter(data, { [`${key}`]: value }));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return !logics[0].filter(v => !v).length && (!logics[1].length || !!logics[1].filter(v => v).length);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const dataTypesValue = [
|
|
120
|
+
'double',
|
|
121
|
+
'string',
|
|
122
|
+
'object',
|
|
123
|
+
'array',
|
|
124
|
+
'decimal',
|
|
125
|
+
// 'long',
|
|
126
|
+
'int',
|
|
127
|
+
'bool',
|
|
128
|
+
'date',
|
|
129
|
+
'null',
|
|
130
|
+
'number'
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const { $IN, $NIN, $GT, $GTE, $LT, $LTE, $EQ, $EXISTS, $REGEX, $NE, $SIZE, $TEXT, $TYPE } = READ_OPS;
|
|
134
|
+
|
|
135
|
+
// TODO: fix exact field value doc, deep nesting and other query
|
|
136
|
+
|
|
137
|
+
const evaluateFilter = (data = {}, filter = {}) => {
|
|
138
|
+
if (!IS_RAW_OBJECT(data)) throw `data must be a raw object`;
|
|
139
|
+
if (!IS_RAW_OBJECT(filter)) throw `expected a raw object but got ${filter}`;
|
|
140
|
+
|
|
141
|
+
const dataObj = { ...data },
|
|
142
|
+
logics = [];
|
|
143
|
+
|
|
144
|
+
queryEntries(filter, []).forEach(([segment, value]) => {
|
|
145
|
+
let commandSplit = segment.map((e, i) => e.startsWith('$') ? ({ $: e, i }) : null).filter(v => v);
|
|
146
|
+
|
|
147
|
+
if (commandSplit.length) {
|
|
148
|
+
const { $, i: dex } = commandSplit[0],
|
|
149
|
+
pathValue = dex ? getLodash(dataObj, segment.filter((_, i) => i < dex).join('.')) : null;
|
|
150
|
+
|
|
151
|
+
if (!READ_OPS_LIST.includes($))
|
|
152
|
+
throw `"${$}" operation is currently not supported`;
|
|
153
|
+
|
|
154
|
+
if ($ !== $TEXT && (dex !== segment.length - 1 || !dex))
|
|
155
|
+
throw `"${$} must be at the last position as an operator"`;
|
|
156
|
+
|
|
157
|
+
if ($ === $IN) {
|
|
158
|
+
if (!Array.isArray(value)) throw `The value assigned to "${$}" operator must be an array`;
|
|
159
|
+
|
|
160
|
+
if (pathValue !== undefined) {
|
|
161
|
+
logics.push(
|
|
162
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
163
|
+
.filter(v => !!value.filter(t => checkTestEquality(t, v)).length).length
|
|
164
|
+
);
|
|
165
|
+
} else logics.push(false);
|
|
166
|
+
} else if ($ === $NIN) {
|
|
167
|
+
if (!Array.isArray(value)) throw `The value assigned to "${$}" operator must be an array`;
|
|
168
|
+
|
|
169
|
+
if (pathValue !== undefined) {
|
|
170
|
+
logics.push(
|
|
171
|
+
!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
172
|
+
.filter(v => !!value.filter(t => checkTestEquality(t, v)).length).length
|
|
173
|
+
);
|
|
174
|
+
} else logics.push(true);
|
|
175
|
+
} else if ($ === $GT) {
|
|
176
|
+
if (pathValue !== undefined) {
|
|
177
|
+
logics.push(
|
|
178
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
179
|
+
.filter(v => v > value).length
|
|
180
|
+
);
|
|
181
|
+
} else logics.push(false);
|
|
182
|
+
} else if ($ === $GTE) {
|
|
183
|
+
if (pathValue !== undefined) {
|
|
184
|
+
logics.push(
|
|
185
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
186
|
+
.filter(v => v >= value).length
|
|
187
|
+
);
|
|
188
|
+
} else logics.push(false);
|
|
189
|
+
} else if ($ === $LT) {
|
|
190
|
+
if (pathValue !== undefined) {
|
|
191
|
+
logics.push(
|
|
192
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
193
|
+
.filter(v => v < value).length
|
|
194
|
+
);
|
|
195
|
+
} else logics.push(false);
|
|
196
|
+
} else if ($ === $LTE) {
|
|
197
|
+
if (pathValue !== undefined) {
|
|
198
|
+
logics.push(
|
|
199
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
200
|
+
.filter(v => v <= value).length
|
|
201
|
+
);
|
|
202
|
+
} else logics.push(false);
|
|
203
|
+
} else if ($ === $TEXT) {
|
|
204
|
+
if (commandSplit.slice(-1)[0].$ === '$search') {
|
|
205
|
+
const { $caseSensitive, $localFields = [], $search } = dataObj.$text;
|
|
206
|
+
|
|
207
|
+
if (typeof value !== 'string' || typeof $search !== 'string')
|
|
208
|
+
throw `$search must have a string value`;
|
|
209
|
+
|
|
210
|
+
const searchTxt = $localFields.map(v => getLodash(dataObj, v || '')).map(v =>
|
|
211
|
+
`${typeof v === 'string' ? v :
|
|
212
|
+
Array.isArray(v) ? v.map(v => typeof v === 'string' ? v : '').join(' ').trim() : ''}`.trim()
|
|
213
|
+
).join(' ').trim();
|
|
214
|
+
|
|
215
|
+
logics.push(
|
|
216
|
+
$caseSensitive ? searchTxt.includes(value.trim()) :
|
|
217
|
+
searchTxt.toLowerCase().includes(value.toLowerCase().trim())
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
} else if ($ === $EQ) {
|
|
221
|
+
|
|
222
|
+
} else if ($ === $EXISTS) {
|
|
223
|
+
|
|
224
|
+
} else if ($ === $REGEX) {
|
|
225
|
+
|
|
226
|
+
} else if ($ === $NE) {
|
|
227
|
+
|
|
228
|
+
} else if ($ === $SIZE) {
|
|
229
|
+
if (!IS_WHOLE_NUMBER(value) || v < 0) throw '$size must be a positive whole number';
|
|
230
|
+
logics.push(Array.isArray(pathValue) && pathValue.length === value);
|
|
231
|
+
} else if ($ === $TYPE) {
|
|
232
|
+
if (!dataTypesValue.includes(value))
|
|
233
|
+
throw `invalid value supplied to ${$}, mosquioto-transport only recognise "${dataTypesValue}" data types`;
|
|
234
|
+
|
|
235
|
+
const cock = (v) => {
|
|
236
|
+
if (typeof v === 'number') {
|
|
237
|
+
if (isNaN(v)) {
|
|
238
|
+
return ((value === 'decimal' || value === 'double') && IS_DECIMAL_NUMBER(v)) || value === 'int' || value === 'number';
|
|
239
|
+
}
|
|
240
|
+
} else if (typeof v === 'boolean') {
|
|
241
|
+
return value === 'bool';
|
|
242
|
+
} else if (typeof v === 'string') {
|
|
243
|
+
return value === 'string';
|
|
244
|
+
} else if (v === null) {
|
|
245
|
+
return value === 'null';
|
|
246
|
+
} else if (v instanceof RegExp) {
|
|
247
|
+
return value === 'regex';
|
|
248
|
+
} else if (v instanceof Date) {
|
|
249
|
+
return value === 'date';
|
|
250
|
+
} else if (IS_RAW_OBJECT(v)) {
|
|
251
|
+
return value === 'object';
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
logics.push(
|
|
257
|
+
(Array.isArray(pathValue) && value === 'array') ||
|
|
258
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
259
|
+
.filter(v => cock(v)).length
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
const pathValue = getLodash(dataObj, segment.join('.'));
|
|
264
|
+
|
|
265
|
+
if (pathValue !== undefined) {
|
|
266
|
+
logics.push(
|
|
267
|
+
!!(Array.isArray(pathValue) ? pathValue : [pathValue])
|
|
268
|
+
.filter(v => !!checkTestEquality(value, v)).length
|
|
269
|
+
);
|
|
270
|
+
} else logics.push(false);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return !logics.filter(v => !v).length;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const checkTestEquality = (test, o) => {
|
|
278
|
+
if (test instanceof RegExp) {
|
|
279
|
+
if (typeof o === 'string') return test.test(o);
|
|
280
|
+
else return false;
|
|
281
|
+
} else return isEqual(test, o);
|
|
282
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
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";
|
|
5
|
+
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
6
|
+
import { awaitRefreshToken } from "../auth/accessor";
|
|
7
|
+
|
|
8
|
+
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,
|
|
25
|
+
status,
|
|
26
|
+
statusText,
|
|
27
|
+
redirected,
|
|
28
|
+
url,
|
|
29
|
+
ok,
|
|
30
|
+
headers: new Headers({ ...headers }),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return h;
|
|
34
|
+
}
|
|
35
|
+
|
|
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 }
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if ('mtoken' in (init?.headers || {}))
|
|
51
|
+
throw '"mtoken" in header is a reserved prop';
|
|
52
|
+
|
|
53
|
+
if ('uglified' in (init?.headers || {}))
|
|
54
|
+
throw '"uglified" in header is a reserved prop';
|
|
55
|
+
|
|
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`;
|
|
61
|
+
|
|
62
|
+
let retries = 0, hasFinalize;
|
|
63
|
+
|
|
64
|
+
const callFetch = () => new Promise(async (resolve, reject) => {
|
|
65
|
+
const retryProcess = ++retries;
|
|
66
|
+
|
|
67
|
+
const finalize = (a, b) => {
|
|
68
|
+
if (a) resolve(a);
|
|
69
|
+
else reject(b);
|
|
70
|
+
if (hasFinalize || retryProcess !== 1) return;
|
|
71
|
+
hasFinalize = true;
|
|
72
|
+
|
|
73
|
+
if (enableMinimizer) {
|
|
74
|
+
(Scoped.PendingFetchCollective.pendingResolution[reqId] || []).forEach(e => {
|
|
75
|
+
e(cloneInstance(a), b);
|
|
76
|
+
});
|
|
77
|
+
if (Scoped.PendingFetchCollective.pendingResolution[reqId])
|
|
78
|
+
delete Scoped.PendingFetchCollective.pendingResolution[reqId];
|
|
79
|
+
|
|
80
|
+
if (Scoped.PendingFetchCollective.pendingProcess[reqId])
|
|
81
|
+
delete Scoped.PendingFetchCollective.pendingProcess[reqId];
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
await awaitStore();
|
|
86
|
+
const reqData = CacheStore.FetchedStore[reqId],
|
|
87
|
+
resolveCache = () => {
|
|
88
|
+
finalize({
|
|
89
|
+
...buildFetchData(reqData),
|
|
90
|
+
fromCache: true
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
|
|
96
|
+
if (retryProcess === 1) {
|
|
97
|
+
if (enableMinimizer) {
|
|
98
|
+
if (Scoped.PendingFetchCollective.pendingProcess[reqId]) {
|
|
99
|
+
if (!Scoped.PendingFetchCollective.pendingResolution[reqId])
|
|
100
|
+
Scoped.PendingFetchCollective.pendingResolution[reqId] = [];
|
|
101
|
+
|
|
102
|
+
Scoped.PendingFetchCollective.pendingResolution[reqId].push((a, b) => {
|
|
103
|
+
if (a) resolve(a.result);
|
|
104
|
+
else reject(b);
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
Scoped.PendingFetchCollective.pendingProcess[reqId] = true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (retrieval.startsWith('sticky') && reqData) {
|
|
112
|
+
resolveCache();
|
|
113
|
+
if (retrieval !== RETRIEVAL.STICKY_RELOAD) return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!disableAuth && await getReachableServer(projectUrl))
|
|
118
|
+
await awaitRefreshToken(projectUrl);
|
|
119
|
+
|
|
120
|
+
const mtoken = Scoped.AuthJWTToken[projectUrl],
|
|
121
|
+
uglified = (!isBaseUrl && init?.body && typeof init?.body === 'string' && uglify),
|
|
122
|
+
initType = extractHeaderItem('content-type', init?.headers);
|
|
123
|
+
|
|
124
|
+
const [reqBuilder, [privateKey]] = (uglified && isBaseUrl) ? serializeE2E(init.body, mtoken, serverE2E_PublicKey) : [null, []];
|
|
125
|
+
|
|
126
|
+
const f = await fetch(isBaseUrl ? input : `${apiUrl}/${input}`, {
|
|
127
|
+
...isBaseUrl ? {} : { method: 'POST' },
|
|
128
|
+
...init,
|
|
129
|
+
...uglified ? { body: reqBuilder } : {},
|
|
130
|
+
cache: 'no-cache',
|
|
131
|
+
headers: {
|
|
132
|
+
...isBaseUrl ? {} : { 'Content-type': 'application/json' },
|
|
133
|
+
...init?.headers,
|
|
134
|
+
...uglified ? {
|
|
135
|
+
uglified,
|
|
136
|
+
'Content-type': 'text/plain',
|
|
137
|
+
...initType ? { 'init-content-type': initType } : {}
|
|
138
|
+
} : {},
|
|
139
|
+
...((disableAuth || !mtoken || uglified || isBaseUrl) ? {} : { mtoken }),
|
|
140
|
+
...isBaseUrl ? {} : { authorization: `Bearer ${accessKey}` }
|
|
141
|
+
}
|
|
142
|
+
}),
|
|
143
|
+
{ ok, type, status, statusText, redirected, url, headers } = f,
|
|
144
|
+
simple = headers.get('simple_error');
|
|
145
|
+
|
|
146
|
+
if (!isBaseUrl && simple) throw { simpleError: JSON.parse(simple) };
|
|
147
|
+
|
|
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
|
+
};
|
|
164
|
+
|
|
165
|
+
if (shouldCache) {
|
|
166
|
+
CacheStore.FetchedStore[reqId] = { ...resObj };
|
|
167
|
+
updateCacheStore();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
finalize(buildFetchData(resObj));
|
|
171
|
+
} catch (e) {
|
|
172
|
+
if (e?.simpleError) {
|
|
173
|
+
finalize(undefined, e.simpleError);
|
|
174
|
+
} else if (
|
|
175
|
+
(retrieval === RETRIEVAL.CACHE_NO_AWAIT && !reqData) ||
|
|
176
|
+
retrieval === RETRIEVAL.STICKY_NO_AWAIT ||
|
|
177
|
+
retrieval === RETRIEVAL.NO_CACHE_NO_AWAIT
|
|
178
|
+
) {
|
|
179
|
+
finalize(undefined, simplifyCaughtError(e).simpleError);
|
|
180
|
+
} else if (
|
|
181
|
+
shouldCache &&
|
|
182
|
+
(retrieval === RETRIEVAL.DEFAULT || retrieval === RETRIEVAL.CACHE_NO_AWAIT) &&
|
|
183
|
+
reqData
|
|
184
|
+
) {
|
|
185
|
+
resolveCache();
|
|
186
|
+
} else if (retries >= maxRetries) {
|
|
187
|
+
finalize(undefined, simplifyCaughtError(e).simpleError);
|
|
188
|
+
} else {
|
|
189
|
+
const listener = listenReachableServer(async online => {
|
|
190
|
+
if (online) {
|
|
191
|
+
listener();
|
|
192
|
+
callFetch().then(
|
|
193
|
+
e => {
|
|
194
|
+
if (retryProcess === 1) {
|
|
195
|
+
finalize(e);
|
|
196
|
+
} else resolve(e);
|
|
197
|
+
},
|
|
198
|
+
e => {
|
|
199
|
+
if (retryProcess === 1) {
|
|
200
|
+
finalize(undefined, e);
|
|
201
|
+
} else reject(e);
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}, projectUrl);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
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
|
+
}
|