skapi-js 0.0.48 → 0.0.49

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/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 (typeof email !== 'string') {
552
- throw new SkapiError(`"${paramName}"should be type: string.`, { code: 'INVALID_EMAIL' });
651
+ else if (struct === null) {
652
+ // bypass value on null
653
+ val = _params;
553
654
  }
554
655
 
555
- else if (email.length < 5 || email.length > 64) {
556
- throw new SkapiError(`"${paramName}" should be at least 5 characters and max 64 characters.`, { code: 'INVALID_EMAIL' });
656
+ if (val === undefined && errToThrow) {
657
+ throw errToThrow;
557
658
  }
558
659
 
559
- else if (/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email)) {
560
- email = email.trim();
561
- let splitAt = email.split('@');
562
- let tld = splitAt[1].split('.');
660
+ return val;
661
+ }
563
662
 
564
- if (tld.length >= 2) {
565
- return email.toLowerCase();
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
- throw new SkapiError(`"${email}" is an invalid email.`, { code: 'INVALID_EMAIL' });
570
- }
680
+ if (form instanceof FormData) {
681
+ let meta = {};
682
+ let totalFileSize = 0;
683
+ let files = [];
571
684
 
572
- function validateUrl(url: string | string[]) {
573
- const baseUrl = (() => {
574
- let baseUrl = window?.location?.origin || null;
685
+ for (let pair of form.entries()) {
686
+ let name = pair[0];
687
+ let v: any = pair[1];
575
688
 
576
- if (baseUrl === 'file://') {
577
- baseUrl += window.location.pathname;
578
- let _baseUrl = baseUrl.split('/');
579
- _baseUrl.pop();
580
- baseUrl = _baseUrl.join('/');
581
- }
689
+ if (v instanceof File) {
690
+ if (!files.includes(name)) {
691
+ files.push(name);
692
+ }
582
693
 
583
- return baseUrl;
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
- if (_url.protocol) {
605
- let url = _url.href;
606
- if (url.charAt(url.length - 1) === '/')
607
- url = url.substring(0, url.length - 1);
697
+ else if (v instanceof FileList) {
698
+ if (!files.includes(name)) {
699
+ files.push(name);
700
+ }
608
701
 
609
- return url;
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
- throw new SkapiError(`"${c}" is an invalid url.`, { code: 'INVALID_PARAMETER' });
616
- };
709
+ else {
710
+ appendData(meta, name, v);
711
+ }
712
+ }
617
713
 
618
- if (Array.isArray(url)) {
619
- return url.map(u => check(u));
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
- function normalize_record_data<T extends RecordData>(record: T): RecordData {
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
- output.config.allow_multiple_reference = r;
689
- },
690
- 'ref_limt': (r) => {
691
- if (!r) return;
692
- if (!output?.config)
693
- output.config = {};
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
- output.config.reference_limit = r;
696
- },
697
- 'rfd': (r) => {
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
- return output as RecordData;
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
- function normalize_typed_string(v: string) {
725
- let value = v.substring(2);
726
- let type = v.substring(0, 2);
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
- switch (type) {
729
- case "S%":
730
- // S%string
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
- function checkWhiteSpaceAndSpecialChars(
751
- string: string | string[],
752
- p = 'parameter',
753
- allowPeriods = false,
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
- if (!allowWhiteSpace && string.includes(' ')) {
762
- throw new SkapiError(`${p} should not have whitespace.`, { code: 'INVALID_PARAMETER' });
763
- }
754
+ else if (i.type === 'file') {
755
+ if (!files.includes(i.name)) {
756
+ files.push(i.name);
757
+ }
764
758
 
765
- if (!allowPeriods && string.includes('.')) {
766
- throw new SkapiError(`${p} should not have periods.`, { code: 'INVALID_PARAMETER' });
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
- if (/[`!@#$%^&*()_+\-=\[\]{};':"\\|,<>\/?~]/.test(s)) {
770
- throw new SkapiError(`${p} should not have special characters.`, { code: 'INVALID_PARAMETER' });
766
+ else {
767
+ appendData(meta, i.name, i.value);
768
+ }
769
+ }
771
770
  }
772
- };
773
771
 
774
- if (Array.isArray(string)) {
775
- for (let s of string) {
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
- else {
781
- checkStr(string, false);
776
+ return { meta, files };
782
777
  }
783
778
 
784
- return string;
779
+ return null;
785
780
  }
786
781
 
787
782
  export {