skapi-js 0.0.48 → 0.0.50
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/deploy.py +7 -2
- package/package.json +6 -5
- package/src/Types.ts +1 -3
- package/src/skapi.ts +51 -193
- package/src/utils.ts +386 -391
- package/dist/skapi.js +0 -3
- package/dist/skapi.js.LICENSE.txt +0 -21
- package/dist/skapi.js.map +0 -1
- package/dist/skapi.module.js +0 -3
- package/dist/skapi.module.js.LICENSE.txt +0 -21
- package/dist/skapi.module.js.map +0 -1
package/src/utils.ts
CHANGED
|
@@ -159,6 +159,299 @@ class MD5 {
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// validation checks
|
|
163
|
+
|
|
164
|
+
function validateUserId(id: string, param = 'User ID') {
|
|
165
|
+
let uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
166
|
+
|
|
167
|
+
if (!id) {
|
|
168
|
+
throw new SkapiError(`${param} is empty.`, { code: 'INVALID_PARAMETER' });
|
|
169
|
+
}
|
|
170
|
+
else if (typeof id !== 'string') {
|
|
171
|
+
throw new SkapiError(`${param} should be type: string.`, { code: 'INVALID_PARAMETER' });
|
|
172
|
+
}
|
|
173
|
+
else if (!id.match(uuid_regex)) {
|
|
174
|
+
throw new SkapiError(`${param} is invalid.`, { code: 'INVALID_PARAMETER' });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return id;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function validatePhoneNumber(value: string) {
|
|
181
|
+
if (typeof value !== 'string' || value.charAt(0) !== '+' || isNaN(Number(value.substring(1)))) {
|
|
182
|
+
throw new SkapiError('"phone_number" is invalid. The format should be "+00123456789". Type: string.', { code: 'INVALID_PARAMETER' });
|
|
183
|
+
}
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function validateBirthdate(birthdate: string) {
|
|
188
|
+
// yyyy-mm-dd
|
|
189
|
+
if (typeof birthdate !== 'string') {
|
|
190
|
+
throw new SkapiError('"birthdate" is invalid. The format should be "yyyy-mm-dd". Type: string.', { code: 'INVALID_PARAMETER' });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
else {
|
|
194
|
+
let date_regex = new RegExp(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
|
|
195
|
+
if (birthdate.length !== 10 || birthdate.split('-').length !== 3 || !date_regex.test(birthdate)) {
|
|
196
|
+
throw new SkapiError('"birthdate" is invalid. The format should be "yyyy-mm-dd". Type: string.', { code: 'INVALID_PARAMETER' });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return birthdate;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function validatePassword(password: string) {
|
|
203
|
+
if (!password) {
|
|
204
|
+
throw new SkapiError('"password" is empty.', { code: 'PASSWORD_REQUIRED' });
|
|
205
|
+
}
|
|
206
|
+
else if (typeof password !== 'string') {
|
|
207
|
+
throw new SkapiError('"password" should be type: string.', { code: 'INVALID_PASSWORD' });
|
|
208
|
+
}
|
|
209
|
+
else if (password.length < 6) {
|
|
210
|
+
throw new SkapiError('"password" should be at least 6 characters.', { code: 'INVALID_PASSWORD' });
|
|
211
|
+
}
|
|
212
|
+
else if (password.length > 60) {
|
|
213
|
+
throw new SkapiError('"password" can be up to 60 characters max.', { code: 'INVALID_PASSWORD' });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return password;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function validateEmail(email: string, paramName: string = 'email') {
|
|
220
|
+
if (!email) {
|
|
221
|
+
throw new SkapiError(`"${paramName}" is empty.`, { code: 'EMAIL_REQUIRED' });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
else if (typeof email !== 'string') {
|
|
225
|
+
throw new SkapiError(`"${paramName}"should be type: string.`, { code: 'INVALID_EMAIL' });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
else if (email.length < 5 || email.length > 64) {
|
|
229
|
+
throw new SkapiError(`"${paramName}" should be at least 5 characters and max 64 characters.`, { code: 'INVALID_EMAIL' });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
else if (/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email)) {
|
|
233
|
+
email = email.trim();
|
|
234
|
+
let splitAt = email.split('@');
|
|
235
|
+
let tld = splitAt[1].split('.');
|
|
236
|
+
|
|
237
|
+
if (tld.length >= 2) {
|
|
238
|
+
return email.toLowerCase();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
throw new SkapiError(`"${email}" is an invalid email.`, { code: 'INVALID_EMAIL' });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function validateUrl(url: string | string[]) {
|
|
246
|
+
const baseUrl = (() => {
|
|
247
|
+
let baseUrl = window?.location?.origin || null;
|
|
248
|
+
|
|
249
|
+
if (baseUrl === 'file://') {
|
|
250
|
+
baseUrl += window.location.pathname;
|
|
251
|
+
let _baseUrl = baseUrl.split('/');
|
|
252
|
+
_baseUrl.pop();
|
|
253
|
+
baseUrl = _baseUrl.join('/');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return baseUrl;
|
|
257
|
+
})();
|
|
258
|
+
let check = (c: string) => {
|
|
259
|
+
if (typeof c === 'string') {
|
|
260
|
+
if (c === '*') {
|
|
261
|
+
return '*';
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
let cu = c.trim();
|
|
265
|
+
if (!cu.includes(' ') && !cu.includes(',')) {
|
|
266
|
+
if (cu.substring(0, 1) === '/' && baseUrl) {
|
|
267
|
+
cu = baseUrl + cu;
|
|
268
|
+
}
|
|
269
|
+
let _url = null;
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
_url = new URL(cu);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
throw new SkapiError(`"${c}" is an invalid url.`, { code: 'INVALID_PARAMETER' });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (_url.protocol) {
|
|
278
|
+
let url = _url.href;
|
|
279
|
+
if (url.charAt(url.length - 1) === '/')
|
|
280
|
+
url = url.substring(0, url.length - 1);
|
|
281
|
+
|
|
282
|
+
return url;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
throw new SkapiError(`"${c}" is an invalid url.`, { code: 'INVALID_PARAMETER' });
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
if (Array.isArray(url)) {
|
|
292
|
+
return url.map(u => check(u));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
return check(url);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function normalize_record_data(record: Record<string, any>): RecordData {
|
|
300
|
+
function base_decode(chars) {
|
|
301
|
+
let charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
302
|
+
return chars.split('').reverse().reduce((prev, curr, i) =>
|
|
303
|
+
prev + (charset.indexOf(curr) * (62 ** i)), 0);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const output: Record<string, any> = {
|
|
307
|
+
config: {
|
|
308
|
+
reference_limit: null
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const keys = {
|
|
313
|
+
'ip': (r: string) => {
|
|
314
|
+
output.ip = r;
|
|
315
|
+
},
|
|
316
|
+
'rec': (r: string) => {
|
|
317
|
+
if (!r) return;
|
|
318
|
+
output.record_id = r;
|
|
319
|
+
let base62timestamp = r.substring(0, r.length - 9); // id: [base62 timestamp][random 5 char][suid]
|
|
320
|
+
let uploaded = base_decode(base62timestamp);
|
|
321
|
+
output.uploaded = uploaded;
|
|
322
|
+
},
|
|
323
|
+
'usr': (r: string) => {
|
|
324
|
+
output.user_id = r;
|
|
325
|
+
},
|
|
326
|
+
'tbl': (r: string) => {
|
|
327
|
+
if (!r) return;
|
|
328
|
+
let rSplit = r.split('/');
|
|
329
|
+
output.table = rSplit[0];
|
|
330
|
+
output.access_group = rSplit[2] == '**' ? 'private' : parseInt(rSplit[2]);
|
|
331
|
+
if (rSplit?.[3]) {
|
|
332
|
+
output.subscription = {
|
|
333
|
+
user_id: rSplit[3],
|
|
334
|
+
group: parseInt(rSplit[4])
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
'idx': (r: string) => {
|
|
339
|
+
if (!r) return;
|
|
340
|
+
let rSplit = r.split('!');
|
|
341
|
+
let name = rSplit.splice(0, 1)[0];
|
|
342
|
+
let value = normalize_typed_string('!' + rSplit.join('!'));
|
|
343
|
+
output.index = {
|
|
344
|
+
name,
|
|
345
|
+
value
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
'ref': (r: string) => {
|
|
349
|
+
if (!r) return;
|
|
350
|
+
output.reference = r.split('/')[0];
|
|
351
|
+
},
|
|
352
|
+
'tags': (r: string[]) => {
|
|
353
|
+
output.tags = r;
|
|
354
|
+
},
|
|
355
|
+
'upd': (r: number) => {
|
|
356
|
+
output.updated = r;
|
|
357
|
+
},
|
|
358
|
+
'acpt_mrf': (r: boolean) => {
|
|
359
|
+
output.config.allow_multiple_reference = r;
|
|
360
|
+
},
|
|
361
|
+
'ref_limt': (r: number) => {
|
|
362
|
+
output.config.reference_limit = r;
|
|
363
|
+
},
|
|
364
|
+
'rfd': (r: number) => {
|
|
365
|
+
output.referenced_count = r;
|
|
366
|
+
},
|
|
367
|
+
'data': (r: any) => {
|
|
368
|
+
let data = r;
|
|
369
|
+
if (r === '!D%{}') {
|
|
370
|
+
data = {};
|
|
371
|
+
}
|
|
372
|
+
else if (r === '!L%[]') {
|
|
373
|
+
data = [];
|
|
374
|
+
}
|
|
375
|
+
output.data = data;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
if (record.record_id) {
|
|
380
|
+
// bypass already normalized records
|
|
381
|
+
return record as RecordData;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
for (let k in keys) {
|
|
385
|
+
keys[k](record[k]);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return output as RecordData;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function normalize_typed_string(v: string) {
|
|
392
|
+
let value = v.substring(3);
|
|
393
|
+
let type = v.substring(0, 3);
|
|
394
|
+
|
|
395
|
+
switch (type) {
|
|
396
|
+
case "!S%":
|
|
397
|
+
// !S%string
|
|
398
|
+
return value;
|
|
399
|
+
case "!N%":
|
|
400
|
+
// !N%0
|
|
401
|
+
return Number(value) - 4503599627370496;
|
|
402
|
+
case "!B%":
|
|
403
|
+
// !B%1
|
|
404
|
+
return value === '1';
|
|
405
|
+
case "!L%":
|
|
406
|
+
case "!D%":
|
|
407
|
+
// !L%[0, "hello"] / !D%{}
|
|
408
|
+
try {
|
|
409
|
+
return JSON.parse(value);
|
|
410
|
+
} catch (err) {
|
|
411
|
+
throw new SkapiError('Value parse error.', { code: 'PARSE_ERROR' });
|
|
412
|
+
}
|
|
413
|
+
default:
|
|
414
|
+
return v;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function checkWhiteSpaceAndSpecialChars(
|
|
419
|
+
string: string | string[],
|
|
420
|
+
p = 'parameter',
|
|
421
|
+
allowPeriods = false,
|
|
422
|
+
allowWhiteSpace = false
|
|
423
|
+
) {
|
|
424
|
+
let checkStr = (s: string) => {
|
|
425
|
+
if (typeof s !== 'string') {
|
|
426
|
+
throw new SkapiError(`${p} should be type: <string | string[]>.`, { code: 'INVALID_PARAMETER' });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!allowWhiteSpace && string.includes(' ')) {
|
|
430
|
+
throw new SkapiError(`${p} should not have whitespace.`, { code: 'INVALID_PARAMETER' });
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (!allowPeriods && string.includes('.')) {
|
|
434
|
+
throw new SkapiError(`${p} should not have periods.`, { code: 'INVALID_PARAMETER' });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (/[`!@#$%^&*()_+\-=\[\]{};':"\\|,<>\/?~]/.test(s)) {
|
|
438
|
+
throw new SkapiError(`${p} should not have special characters.`, { code: 'INVALID_PARAMETER' });
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
if (Array.isArray(string)) {
|
|
443
|
+
for (let s of string) {
|
|
444
|
+
checkStr(s);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
else {
|
|
449
|
+
checkStr(string);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return string;
|
|
453
|
+
}
|
|
454
|
+
|
|
162
455
|
function checkParams(
|
|
163
456
|
params: any,
|
|
164
457
|
struct: Record<string, any>,
|
|
@@ -351,437 +644,139 @@ function checkParams(
|
|
|
351
644
|
val = _params;
|
|
352
645
|
}
|
|
353
646
|
else {
|
|
354
|
-
throw new SkapiError(`Value: ${_params}${isInvalid}`, { code: 'INVALID_PARAMETER' });
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
else if (struct === null) {
|
|
359
|
-
// bypass value on null
|
|
360
|
-
val = _params;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (val === undefined && errToThrow) {
|
|
364
|
-
throw errToThrow;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return val;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function extractFormMetaData(form: Form) {
|
|
371
|
-
// creates meta object to post
|
|
372
|
-
|
|
373
|
-
function appendData(meta, key, val, append = true) {
|
|
374
|
-
if (meta[key] && append) {
|
|
375
|
-
if (Array.isArray(meta)) {
|
|
376
|
-
meta[key].push(val);
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
meta[key] = [meta[key], val];
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
meta[key] = val;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (form instanceof FormData) {
|
|
388
|
-
let meta = {};
|
|
389
|
-
let totalFileSize = 0;
|
|
390
|
-
let files = [];
|
|
391
|
-
|
|
392
|
-
for (let pair of form.entries()) {
|
|
393
|
-
let name = pair[0];
|
|
394
|
-
let v: any = pair[1];
|
|
395
|
-
|
|
396
|
-
if (v instanceof File) {
|
|
397
|
-
if (!files.includes(name)) {
|
|
398
|
-
files.push(name);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
totalFileSize += Math.round((v.size / 1024));
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
else if (v instanceof FileList) {
|
|
405
|
-
if (!files.includes(name)) {
|
|
406
|
-
files.push(name);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (v && v.length > 0) {
|
|
410
|
-
for (let idx = 0; idx <= v.length - 1; idx++) {
|
|
411
|
-
totalFileSize += Math.round((v.item(idx).size / 1024));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
else {
|
|
417
|
-
appendData(meta, name, v);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (totalFileSize > 5120) {
|
|
422
|
-
throw new SkapiError('Files cannot exceed 5MB. Use skapi.uploadFiles(...) instead.', { code: 'INVALID_REQUEST' });
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return { meta, files };
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
else if (form instanceof HTMLFormElement) {
|
|
429
|
-
let meta = {};
|
|
430
|
-
let files = [];
|
|
431
|
-
let totalFileSize = 0;
|
|
432
|
-
let inputs = form.querySelectorAll('input');
|
|
433
|
-
let textarea = form.querySelectorAll('textarea');
|
|
434
|
-
|
|
435
|
-
for (let i of textarea) {
|
|
436
|
-
if (i.name) {
|
|
437
|
-
appendData(meta, i.name, i.value);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
for (let i of inputs) {
|
|
442
|
-
if (i.name) {
|
|
443
|
-
if (i.type === 'number' && i.value) {
|
|
444
|
-
appendData(meta, i.name, Number(i.value));
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
else if (i.type === 'checkbox' || i.type === 'radio') {
|
|
448
|
-
if (i.value === 'on' || i.value === 'true') {
|
|
449
|
-
appendData(meta, i.name, i.checked, false);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
else if (i.value === 'false') {
|
|
453
|
-
appendData(meta, i.name, !i.checked, false);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
else if (i.checked) {
|
|
457
|
-
appendData(meta, i.name, i.value, false);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
else if (i.type === 'file') {
|
|
462
|
-
if (!files.includes(i.name)) {
|
|
463
|
-
files.push(i.name);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (i.files && i.files.length > 0) {
|
|
467
|
-
for (let idx = 0; idx <= i.files.length - 1; idx++) {
|
|
468
|
-
totalFileSize += Math.round((i.files.item(idx).size / 1024));
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
else {
|
|
474
|
-
appendData(meta, i.name, i.value);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (totalFileSize > 5120) {
|
|
480
|
-
throw new SkapiError('Files cannot exceed 5MB. Use skapi.uploadFiles(...) instead.', { code: 'INVALID_REQUEST' });
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return { meta, files };
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// validation checks
|
|
490
|
-
|
|
491
|
-
function validateUserId(id: string, param = 'User ID') {
|
|
492
|
-
let uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
493
|
-
|
|
494
|
-
if (!id) {
|
|
495
|
-
throw new SkapiError(`${param} is empty.`, { code: 'INVALID_PARAMETER' });
|
|
496
|
-
}
|
|
497
|
-
else if (typeof id !== 'string') {
|
|
498
|
-
throw new SkapiError(`${param} should be type: string.`, { code: 'INVALID_PARAMETER' });
|
|
499
|
-
}
|
|
500
|
-
else if (!id.match(uuid_regex)) {
|
|
501
|
-
throw new SkapiError(`${param} is invalid.`, { code: 'INVALID_PARAMETER' });
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return id;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function validatePhoneNumber(value: string) {
|
|
508
|
-
if (typeof value !== 'string' || value.charAt(0) !== '+' || isNaN(Number(value.substring(1)))) {
|
|
509
|
-
throw new SkapiError('"phone_number" is invalid. The format should be "+00123456789". Type: string.', { code: 'INVALID_PARAMETER' });
|
|
510
|
-
}
|
|
511
|
-
return value;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function validateBirthdate(birthdate: string) {
|
|
515
|
-
// yyyy-mm-dd
|
|
516
|
-
if (typeof birthdate !== 'string') {
|
|
517
|
-
throw new SkapiError('"birthdate" is invalid. The format should be "yyyy-mm-dd". Type: string.', { code: 'INVALID_PARAMETER' });
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
else {
|
|
521
|
-
let date_regex = new RegExp(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
|
|
522
|
-
if (birthdate.length !== 10 || birthdate.split('-').length !== 3 || !date_regex.test(birthdate)) {
|
|
523
|
-
throw new SkapiError('"birthdate" is invalid. The format should be "yyyy-mm-dd". Type: string.', { code: 'INVALID_PARAMETER' });
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return birthdate;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
function validatePassword(password: string) {
|
|
530
|
-
if (!password) {
|
|
531
|
-
throw new SkapiError('"password" is empty.', { code: 'PASSWORD_REQUIRED' });
|
|
532
|
-
}
|
|
533
|
-
else if (typeof password !== 'string') {
|
|
534
|
-
throw new SkapiError('"password" should be type: string.', { code: 'INVALID_PASSWORD' });
|
|
535
|
-
}
|
|
536
|
-
else if (password.length < 6) {
|
|
537
|
-
throw new SkapiError('"password" should be at least 6 characters.', { code: 'INVALID_PASSWORD' });
|
|
538
|
-
}
|
|
539
|
-
else if (password.length > 60) {
|
|
540
|
-
throw new SkapiError('"password" can be up to 60 characters max.', { code: 'INVALID_PASSWORD' });
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
return password;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function validateEmail(email: string, paramName: string = 'email') {
|
|
547
|
-
if (!email) {
|
|
548
|
-
throw new SkapiError(`"${paramName}" is empty.`, { code: 'EMAIL_REQUIRED' });
|
|
647
|
+
throw new SkapiError(`Value: ${_params}${isInvalid}`, { code: 'INVALID_PARAMETER' });
|
|
648
|
+
}
|
|
549
649
|
}
|
|
550
650
|
|
|
551
|
-
else if (
|
|
552
|
-
|
|
651
|
+
else if (struct === null) {
|
|
652
|
+
// bypass value on null
|
|
653
|
+
val = _params;
|
|
553
654
|
}
|
|
554
655
|
|
|
555
|
-
|
|
556
|
-
throw
|
|
656
|
+
if (val === undefined && errToThrow) {
|
|
657
|
+
throw errToThrow;
|
|
557
658
|
}
|
|
558
659
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
let splitAt = email.split('@');
|
|
562
|
-
let tld = splitAt[1].split('.');
|
|
660
|
+
return val;
|
|
661
|
+
}
|
|
563
662
|
|
|
564
|
-
|
|
565
|
-
|
|
663
|
+
function extractFormMetaData(form: Form) {
|
|
664
|
+
// creates meta object to post
|
|
665
|
+
|
|
666
|
+
function appendData(meta, key, val, append = true) {
|
|
667
|
+
if (meta[key] && append) {
|
|
668
|
+
if (Array.isArray(meta)) {
|
|
669
|
+
meta[key].push(val);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
meta[key] = [meta[key], val];
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
meta[key] = val;
|
|
566
677
|
}
|
|
567
678
|
}
|
|
568
679
|
|
|
569
|
-
|
|
570
|
-
}
|
|
680
|
+
if (form instanceof FormData) {
|
|
681
|
+
let meta = {};
|
|
682
|
+
let totalFileSize = 0;
|
|
683
|
+
let files = [];
|
|
571
684
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
685
|
+
for (let pair of form.entries()) {
|
|
686
|
+
let name = pair[0];
|
|
687
|
+
let v: any = pair[1];
|
|
575
688
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
baseUrl = _baseUrl.join('/');
|
|
581
|
-
}
|
|
689
|
+
if (v instanceof File) {
|
|
690
|
+
if (!files.includes(name)) {
|
|
691
|
+
files.push(name);
|
|
692
|
+
}
|
|
582
693
|
|
|
583
|
-
|
|
584
|
-
})();
|
|
585
|
-
let check = (c: string) => {
|
|
586
|
-
if (typeof c === 'string') {
|
|
587
|
-
if (c === '*') {
|
|
588
|
-
return '*';
|
|
694
|
+
totalFileSize += Math.round((v.size / 1024));
|
|
589
695
|
}
|
|
590
|
-
else {
|
|
591
|
-
let cu = c.trim();
|
|
592
|
-
if (!cu.includes(' ') && !cu.includes(',')) {
|
|
593
|
-
if (cu.substring(0, 1) === '/' && baseUrl) {
|
|
594
|
-
cu = baseUrl + cu;
|
|
595
|
-
}
|
|
596
|
-
let _url = null;
|
|
597
|
-
|
|
598
|
-
try {
|
|
599
|
-
_url = new URL(cu);
|
|
600
|
-
} catch (err) {
|
|
601
|
-
throw new SkapiError(`"${c}" is an invalid url.`, { code: 'INVALID_PARAMETER' });
|
|
602
|
-
}
|
|
603
696
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
697
|
+
else if (v instanceof FileList) {
|
|
698
|
+
if (!files.includes(name)) {
|
|
699
|
+
files.push(name);
|
|
700
|
+
}
|
|
608
701
|
|
|
609
|
-
|
|
702
|
+
if (v && v.length > 0) {
|
|
703
|
+
for (let idx = 0; idx <= v.length - 1; idx++) {
|
|
704
|
+
totalFileSize += Math.round((v.item(idx).size / 1024));
|
|
610
705
|
}
|
|
611
706
|
}
|
|
612
707
|
}
|
|
613
|
-
}
|
|
614
708
|
|
|
615
|
-
|
|
616
|
-
|
|
709
|
+
else {
|
|
710
|
+
appendData(meta, name, v);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
617
713
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
else {
|
|
622
|
-
return check(url);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
714
|
+
if (totalFileSize > 5120) {
|
|
715
|
+
throw new SkapiError('Files cannot exceed 5MB. Use skapi.uploadFiles(...) instead.', { code: 'INVALID_REQUEST' });
|
|
716
|
+
}
|
|
625
717
|
|
|
626
|
-
|
|
627
|
-
function base_decode(chars) {
|
|
628
|
-
let charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
629
|
-
return chars.split('').reverse().reduce((prev, curr, i) =>
|
|
630
|
-
prev + (charset.indexOf(curr) * (62 ** i)), 0);
|
|
718
|
+
return { meta, files };
|
|
631
719
|
}
|
|
632
|
-
const output: Record<any, any> = {};
|
|
633
|
-
|
|
634
|
-
const keys = {
|
|
635
|
-
'ip': (r) => {
|
|
636
|
-
output.ip = r;
|
|
637
|
-
},
|
|
638
|
-
'rec': (r) => {
|
|
639
|
-
if (!r) return;
|
|
640
|
-
output.record_id = r;
|
|
641
|
-
let base62timestamp = r.substring(0, r.length - 9); // id: [base62 timestamp][random 5 char][suid]
|
|
642
|
-
let uploaded = base_decode(base62timestamp);
|
|
643
|
-
output.uploaded = uploaded;
|
|
644
|
-
},
|
|
645
|
-
'usr': (r) => {
|
|
646
|
-
if (!r) return;
|
|
647
|
-
output.user_id = r;
|
|
648
|
-
},
|
|
649
|
-
'tbl': (r) => {
|
|
650
|
-
if (!r) return;
|
|
651
|
-
let rSplit = r.split('/');
|
|
652
|
-
output.table = rSplit[0];
|
|
653
|
-
output.access_group = rSplit[2] == '**' ? 'private' : parseInt(rSplit[2]);
|
|
654
|
-
if (rSplit?.[3]) {
|
|
655
|
-
output.subscription = {
|
|
656
|
-
user_id: rSplit[3],
|
|
657
|
-
group: parseInt(rSplit[4])
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
'idx': (r) => {
|
|
662
|
-
if (!r) return;
|
|
663
|
-
let rSplit = r.split('!');
|
|
664
|
-
let name = rSplit.splice(0, 1)[0];
|
|
665
|
-
let value = normalize_typed_string(rSplit.join('!'));
|
|
666
|
-
output.index = {
|
|
667
|
-
name,
|
|
668
|
-
value
|
|
669
|
-
};
|
|
670
|
-
},
|
|
671
|
-
'ref': (r) => {
|
|
672
|
-
if (!r) return;
|
|
673
|
-
output.reference = r.split('/')[0];
|
|
674
|
-
},
|
|
675
|
-
'tags': (r) => {
|
|
676
|
-
if (!r) return;
|
|
677
|
-
output.tags = r;
|
|
678
|
-
},
|
|
679
|
-
'upd': (r) => {
|
|
680
|
-
if (!r) return;
|
|
681
|
-
output.updated = r;
|
|
682
|
-
},
|
|
683
|
-
'acpt_mrf': (r) => {
|
|
684
|
-
if (!r) return;
|
|
685
|
-
if (!output?.config)
|
|
686
|
-
output.config = {};
|
|
687
720
|
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
721
|
+
else if (form instanceof HTMLFormElement) {
|
|
722
|
+
let meta = {};
|
|
723
|
+
let files = [];
|
|
724
|
+
let totalFileSize = 0;
|
|
725
|
+
let inputs = form.querySelectorAll('input');
|
|
726
|
+
let textarea = form.querySelectorAll('textarea');
|
|
694
727
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
output.referenced_count = r;
|
|
699
|
-
},
|
|
700
|
-
'data': (r) => {
|
|
701
|
-
let data = r;
|
|
702
|
-
if (r === '!D%{}') {
|
|
703
|
-
data = {};
|
|
704
|
-
}
|
|
705
|
-
else if (r === '!L%[]') {
|
|
706
|
-
data = [];
|
|
728
|
+
for (let i of textarea) {
|
|
729
|
+
if (i.name) {
|
|
730
|
+
appendData(meta, i.name, i.value);
|
|
707
731
|
}
|
|
708
|
-
output.data = data;
|
|
709
732
|
}
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
if (record.record_id) {
|
|
713
|
-
// bypass already normalized records
|
|
714
|
-
return record;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
for (let k in keys) {
|
|
718
|
-
keys[k](record[k]);
|
|
719
|
-
}
|
|
720
733
|
|
|
721
|
-
|
|
722
|
-
|
|
734
|
+
for (let i of inputs) {
|
|
735
|
+
if (i.name) {
|
|
736
|
+
if (i.type === 'number' && i.value) {
|
|
737
|
+
appendData(meta, i.name, Number(i.value));
|
|
738
|
+
}
|
|
723
739
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
740
|
+
else if (i.type === 'checkbox' || i.type === 'radio') {
|
|
741
|
+
if (i.value === 'on' || i.value === 'true') {
|
|
742
|
+
appendData(meta, i.name, i.checked, false);
|
|
743
|
+
}
|
|
727
744
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
return value;
|
|
732
|
-
case "N%":
|
|
733
|
-
// N%0
|
|
734
|
-
return Number(value) - 4503599627370496;
|
|
735
|
-
case "B%":
|
|
736
|
-
// B%1
|
|
737
|
-
return value === '1';
|
|
738
|
-
case "L%":
|
|
739
|
-
// L%[0, "hello"]
|
|
740
|
-
try {
|
|
741
|
-
return JSON.parse(value);
|
|
742
|
-
} catch (err) {
|
|
743
|
-
throw new SkapiError('Value parse error.', { code: 'PARSE_ERROR' });
|
|
744
|
-
}
|
|
745
|
-
default:
|
|
746
|
-
return v;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
745
|
+
else if (i.value === 'false') {
|
|
746
|
+
appendData(meta, i.name, !i.checked, false);
|
|
747
|
+
}
|
|
749
748
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
allowWhiteSpace = false
|
|
755
|
-
) {
|
|
756
|
-
let checkStr = (s: string, array = false) => {
|
|
757
|
-
if (typeof s !== 'string') {
|
|
758
|
-
throw new SkapiError(`${p} should be type: <string | string[]>.`, { code: 'INVALID_PARAMETER' });
|
|
759
|
-
}
|
|
749
|
+
else if (i.checked) {
|
|
750
|
+
appendData(meta, i.name, i.value, false);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
760
753
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
754
|
+
else if (i.type === 'file') {
|
|
755
|
+
if (!files.includes(i.name)) {
|
|
756
|
+
files.push(i.name);
|
|
757
|
+
}
|
|
764
758
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
759
|
+
if (i.files && i.files.length > 0) {
|
|
760
|
+
for (let idx = 0; idx <= i.files.length - 1; idx++) {
|
|
761
|
+
totalFileSize += Math.round((i.files.item(idx).size / 1024));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
768
765
|
|
|
769
|
-
|
|
770
|
-
|
|
766
|
+
else {
|
|
767
|
+
appendData(meta, i.name, i.value);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
771
770
|
}
|
|
772
|
-
};
|
|
773
771
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
checkStr(s, true);
|
|
772
|
+
if (totalFileSize > 5120) {
|
|
773
|
+
throw new SkapiError('Files cannot exceed 5MB. Use skapi.uploadFiles(...) instead.', { code: 'INVALID_REQUEST' });
|
|
777
774
|
}
|
|
778
|
-
}
|
|
779
775
|
|
|
780
|
-
|
|
781
|
-
checkStr(string, false);
|
|
776
|
+
return { meta, files };
|
|
782
777
|
}
|
|
783
778
|
|
|
784
|
-
return
|
|
779
|
+
return null;
|
|
785
780
|
}
|
|
786
781
|
|
|
787
782
|
export {
|