@jd-data-limited/easy-fm 3.0.9 → 3.1.0

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.
Files changed (67) hide show
  1. package/.idea/compiler.xml +7 -0
  2. package/.idea/discord.xml +7 -0
  3. package/dist/index.min.js +2 -0
  4. package/dist/index.min.js.LICENSE.txt +3 -0
  5. package/examples/test.js +7 -0
  6. package/img.png +0 -0
  7. package/package.json +1 -1
  8. package/src/FMError.d.ts +12 -0
  9. package/src/FMError.js +1028 -0
  10. package/src/FMError.ts +1035 -0
  11. package/src/connection/FMHost.d.ts +12 -0
  12. package/src/connection/FMHost.js +68 -0
  13. package/src/connection/FMHost.ts +75 -0
  14. package/src/connection/database.d.ts +26 -0
  15. package/src/connection/database.js +195 -0
  16. package/src/connection/database.ts +216 -0
  17. package/src/connection/generateAuthorizationHeaders.d.ts +2 -0
  18. package/src/connection/generateAuthorizationHeaders.js +23 -0
  19. package/src/connection/generateAuthorizationHeaders.ts +32 -0
  20. package/src/index.d.ts +2 -0
  21. package/src/index.js +5 -2073
  22. package/src/index.ts +3 -2323
  23. package/src/layouts/layout.d.ts +29 -0
  24. package/src/layouts/layout.js +84 -0
  25. package/src/layouts/layout.ts +92 -0
  26. package/src/layouts/layoutInterface.d.ts +13 -0
  27. package/src/layouts/layoutInterface.js +4 -0
  28. package/src/layouts/layoutInterface.ts +21 -0
  29. package/src/layouts/layoutRecordManager.d.ts +13 -0
  30. package/src/layouts/layoutRecordManager.js +50 -0
  31. package/src/layouts/layoutRecordManager.ts +59 -0
  32. package/src/records/field.d.ts +21 -0
  33. package/src/records/field.js +177 -0
  34. package/src/records/field.ts +189 -0
  35. package/src/records/getOperations/find.d.ts +15 -0
  36. package/src/records/getOperations/find.js +90 -0
  37. package/src/records/getOperations/find.ts +88 -0
  38. package/src/records/getOperations/recordGetOperation.d.ts +17 -0
  39. package/src/records/getOperations/recordGetOperation.js +43 -0
  40. package/src/records/getOperations/recordGetOperation.ts +54 -0
  41. package/src/records/getOperations/recordGetRange.d.ts +13 -0
  42. package/src/records/getOperations/recordGetRange.js +95 -0
  43. package/src/records/getOperations/recordGetRange.ts +84 -0
  44. package/src/records/layoutRecord.d.ts +16 -0
  45. package/src/records/layoutRecord.js +211 -0
  46. package/src/records/layoutRecord.ts +220 -0
  47. package/src/records/portal.d.ts +10 -0
  48. package/src/records/portal.js +19 -0
  49. package/src/records/portal.ts +28 -0
  50. package/src/records/portalRecord.d.ts +12 -0
  51. package/src/records/portalRecord.js +32 -0
  52. package/src/records/portalRecord.ts +43 -0
  53. package/src/records/recordBase.d.ts +20 -0
  54. package/src/records/recordBase.js +95 -0
  55. package/src/records/recordBase.ts +121 -0
  56. package/src/types.d.ts +117 -0
  57. package/src/types.js +8 -0
  58. package/src/types.ts +151 -0
  59. package/test.d.ts +1 -0
  60. package/test.js +4 -0
  61. package/tsconfig.json +4 -0
  62. package/TEMP/FILEMAKER.txt +0 -218
  63. package/TEMP/WEBAPP.txt +0 -92
  64. package/TEMP/config.yaml +0 -40
  65. package/TEMP/data.json +0 -19837
  66. package/temp2/1.19.2-forge-43.2.8.json +0 -347
  67. package/temp2/Quinn-1.19.2-forge-43.2.8.json +0 -347
package/src/index.ts CHANGED
@@ -1,2327 +1,7 @@
1
1
  /*
2
- * Copyright (c) 2022-2023. See LICENSE file for more information
2
+ * Copyright (c) 2023. See LICENSE file for more information
3
3
  */
4
4
 
5
- // const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
6
- import fetch, {
7
- Blob,
8
- blobFrom,
9
- blobFromSync,
10
- File,
11
- fileFrom,
12
- fileFromSync,
13
- FormData,
14
- Headers, HeadersInit,
15
- Request,
16
- Response
17
- } from 'node-fetch';
18
- import {EventEmitter} from "events";
19
- // @ts-ignore
20
- import * as moment from "moment"
21
- import * as http from "http";
22
- import * as https from "https";
5
+ import FMHost from "./connection/FMHost";
23
6
 
24
- // import * as btoa from "btoa";
25
- interface databaseOptionsBase {
26
- database: string
27
- credentials: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris | loginOptionsToken,
28
- }
29
-
30
- interface databaseOptionsWithExternalSources extends databaseOptionsBase {
31
- database: string
32
- credentials: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris | loginOptionsToken,
33
- externalSources: databaseOptionsBase[]
34
- }
35
-
36
- interface loginOptionsToken {
37
- method: "token"
38
- token: string
39
- }
40
-
41
- interface loginOptionsOAuth {
42
- method: "oauth"
43
- oauth: {
44
- requestId: string,
45
- requestIdentifier: string
46
- }
47
- }
48
-
49
- interface loginOptionsFileMaker {
50
- method: "filemaker"
51
- username: string,
52
- password: string,
53
- }
54
-
55
- interface loginOptionsClaris {
56
- method: "claris"
57
- claris: {
58
- fmid: string
59
- }
60
- }
61
-
62
- interface limitPortalsInterface {
63
- portal: Portal,
64
- offset: number,
65
- limit: number
66
- }
67
-
68
- interface extraBodyOptions {
69
- scripts?: {
70
- prerequest?: Script,
71
- presort?: Script,
72
- after?: Script,
73
- }
74
- }
75
-
76
- export enum DOWNLOAD_MODES {
77
- Stream,
78
- Buffer
79
- }
80
-
81
- // @ts-ignore
82
- const errs = [
83
- {
84
- "e": -1,
85
- "d": "Unknown error"
86
- },
87
- {
88
- "e": 0,
89
- "d": "No error"
90
- },
91
- {
92
- "e": 1,
93
- "d": "User canceled action"
94
- },
95
- {
96
- "e": 2,
97
- "d": "Memory error"
98
- },
99
- {
100
- "e": 3,
101
- "d": "Command is unavailable (for example, wrong operating system or mode)"
102
- },
103
- {
104
- "e": 4,
105
- "d": "Command is unknown"
106
- },
107
- {
108
- "e": 5,
109
- "d": "Command is invalid (for example, a Set Field script step does not have a calculation specified)"
110
- },
111
- {
112
- "e": 6,
113
- "d": "File is read-only"
114
- },
115
- {
116
- "e": 7,
117
- "d": "Running out of memory"
118
- },
119
- {
120
- "e": 8,
121
- "d": "Empty result"
122
- },
123
- {
124
- "e": 9,
125
- "d": "Insufficient privileges"
126
- },
127
- {
128
- "e": 10,
129
- "d": "Requested data is missing"
130
- },
131
- {
132
- "e": 11,
133
- "d": "Name is not valid"
134
- },
135
- {
136
- "e": 12,
137
- "d": "Name already exists"
138
- },
139
- {
140
- "e": 13,
141
- "d": "File or object is in use"
142
- },
143
- {
144
- "e": 14,
145
- "d": "Out of range"
146
- },
147
- {
148
- "e": 15,
149
- "d": "Can't divide by zero"
150
- },
151
- {
152
- "e": 16,
153
- "d": "Operation failed; request retry (for example, a user query)"
154
- },
155
- {
156
- "e": 17,
157
- "d": "Attempt to convert foreign character set to UTF-16 failed"
158
- },
159
- {
160
- "e": 18,
161
- "d": "Client must provide account information to proceed"
162
- },
163
- {
164
- "e": 19,
165
- "d": "String contains characters other than A-Z, a-z, 0-9 (ASCII)"
166
- },
167
- {
168
- "e": 20,
169
- "d": "Command/operation canceled by triggered script"
170
- },
171
- {
172
- "e": 21,
173
- "d": "Request not supported (for example, when creating a hard link on a file system that does not support hard links)"
174
- },
175
- {
176
- "e": 100,
177
- "d": "File is missing"
178
- },
179
- {
180
- "e": 101,
181
- "d": "Record is missing"
182
- },
183
- {
184
- "e": 102,
185
- "d": "Field is missing"
186
- },
187
- {
188
- "e": 103,
189
- "d": "Relationship is missing"
190
- },
191
- {
192
- "e": 104,
193
- "d": "Script is missing"
194
- },
195
- {
196
- "e": 105,
197
- "d": "Layout is missing"
198
- },
199
- {
200
- "e": 106,
201
- "d": "Table is missing"
202
- },
203
- {
204
- "e": 107,
205
- "d": "Index is missing"
206
- },
207
- {
208
- "e": 108,
209
- "d": "Value list is missing"
210
- },
211
- {
212
- "e": 109,
213
- "d": "Privilege set is missing"
214
- },
215
- {
216
- "e": 110,
217
- "d": "Related tables are missing"
218
- },
219
- {
220
- "e": 111,
221
- "d": "Field repetition is invalid"
222
- },
223
- {
224
- "e": 112,
225
- "d": "Window is missing"
226
- },
227
- {
228
- "e": 113,
229
- "d": "Function is missing"
230
- },
231
- {
232
- "e": 114,
233
- "d": "File reference is missing"
234
- },
235
- {
236
- "e": 115,
237
- "d": "Menu set is missing"
238
- },
239
- {
240
- "e": 116,
241
- "d": "Layout object is missing"
242
- },
243
- {
244
- "e": 117,
245
- "d": "Data source is missing"
246
- },
247
- {
248
- "e": 118,
249
- "d": "Theme is missing"
250
- },
251
- {
252
- "e": 130,
253
- "d": "Files are damaged or missing and must be reinstalled"
254
- },
255
- {
256
- "e": 131,
257
- "d": "Language pack files are missing"
258
- },
259
- {
260
- "e": 200,
261
- "d": "Record access is denied"
262
- },
263
- {
264
- "e": 201,
265
- "d": "Field cannot be modified"
266
- },
267
- {
268
- "e": 202,
269
- "d": "Field access is denied"
270
- },
271
- {
272
- "e": 203,
273
- "d": "No records in file to print, or password doesn't allow print access"
274
- },
275
- {
276
- "e": 204,
277
- "d": "No access to field(s) in sort order"
278
- },
279
- {
280
- "e": 205,
281
- "d": "User does not have access privileges to create new records; import will overwrite existing data"
282
- },
283
- {
284
- "e": 206,
285
- "d": "User does not have password change privileges, or file is not modifiable"
286
- },
287
- {
288
- "e": 207,
289
- "d": "User does not have privileges to change database schema, or file is not modifiable"
290
- },
291
- {
292
- "e": 208,
293
- "d": "Password does not contain enough characters"
294
- },
295
- {
296
- "e": 209,
297
- "d": "New password must be different from existing one"
298
- },
299
- {
300
- "e": 210,
301
- "d": "User account is inactive"
302
- },
303
- {
304
- "e": 211,
305
- "d": "Password has expired "
306
- },
307
- {
308
- "e": 212,
309
- "d": "Invalid user account and/or password; please try again"
310
- },
311
- {
312
- "e": 214,
313
- "d": "Too many login attempts"
314
- },
315
- {
316
- "e": 215,
317
- "d": "Administrator privileges cannot be duplicated"
318
- },
319
- {
320
- "e": 216,
321
- "d": "Guest account cannot be duplicated"
322
- },
323
- {
324
- "e": 217,
325
- "d": "User does not have sufficient privileges to modify administrator account"
326
- },
327
- {
328
- "e": 218,
329
- "d": "Password and verify password do not match"
330
- },
331
- {
332
- "e": 300,
333
- "d": "File is locked or in use"
334
- },
335
- {
336
- "e": 301,
337
- "d": "Record is in use by another user"
338
- },
339
- {
340
- "e": 302,
341
- "d": "Table is in use by another user"
342
- },
343
- {
344
- "e": 303,
345
- "d": "Database schema is in use by another user"
346
- },
347
- {
348
- "e": 304,
349
- "d": "Layout is in use by another user"
350
- },
351
- {
352
- "e": 306,
353
- "d": "Record modification ID does not match"
354
- },
355
- {
356
- "e": 307,
357
- "d": "Transaction could not be locked because of a communication error with the host"
358
- },
359
- {
360
- "e": 308,
361
- "d": "Theme is locked and in use by another user"
362
- },
363
- {
364
- "e": 400,
365
- "d": "Find criteria are empty"
366
- },
367
- {
368
- "e": 401,
369
- "d": "No records match the request"
370
- },
371
- {
372
- "e": 402,
373
- "d": "Selected field is not a match field for a lookup"
374
- },
375
- {
376
- "e": 404,
377
- "d": "Sort order is invalid"
378
- },
379
- {
380
- "e": 405,
381
- "d": "Number of records specified exceeds number of records that can be omitted"
382
- },
383
- {
384
- "e": 406,
385
- "d": "Replace/reserialize criteria are invalid"
386
- },
387
- {
388
- "e": 407,
389
- "d": "One or both match fields are missing (invalid relationship)"
390
- },
391
- {
392
- "e": 408,
393
- "d": "Specified field has inappropriate data type for this operation"
394
- },
395
- {
396
- "e": 409,
397
- "d": "Import order is invalid"
398
- },
399
- {
400
- "e": 410,
401
- "d": "Export order is invalid"
402
- },
403
- {
404
- "e": 412,
405
- "d": "Wrong version of FileMaker Pro Advanced used to recover file"
406
- },
407
- {
408
- "e": 413,
409
- "d": "Specified field has inappropriate field type"
410
- },
411
- {
412
- "e": 414,
413
- "d": "Layout cannot display the result"
414
- },
415
- {
416
- "e": 415,
417
- "d": "One or more required related records are not available"
418
- },
419
- {
420
- "e": 416,
421
- "d": "A primary key is required from the data source table"
422
- },
423
- {
424
- "e": 417,
425
- "d": "File is not a supported data source"
426
- },
427
- {
428
- "e": 418,
429
- "d": "Internal failure in INSERT operation into a field"
430
- },
431
- {
432
- "e": 500,
433
- "d": "Date value does not meet validation entry options"
434
- },
435
- {
436
- "e": 501,
437
- "d": "Time value does not meet validation entry options"
438
- },
439
- {
440
- "e": 502,
441
- "d": "Number value does not meet validation entry options"
442
- },
443
- {
444
- "e": 503,
445
- "d": "Value in field is not within the range specified in validation entry options"
446
- },
447
- {
448
- "e": 504,
449
- "d": "Value in field is not unique, as required in validation entry options"
450
- },
451
- {
452
- "e": 505,
453
- "d": "Value in field is not an existing value in the file, as required in validation entry options"
454
- },
455
- {
456
- "e": 506,
457
- "d": "Value in field is not listed in the value list specified in validation entry option"
458
- },
459
- {
460
- "e": 507,
461
- "d": "Value in field failed calculation test of validation entry option"
462
- },
463
- {
464
- "e": 508,
465
- "d": "Invalid value entered in Find mode"
466
- },
467
- {
468
- "e": 509,
469
- "d": "Field requires a valid value"
470
- },
471
- {
472
- "e": 510,
473
- "d": "Related value is empty or unavailable"
474
- },
475
- {
476
- "e": 511,
477
- "d": "Value in field exceeds maximum field size"
478
- },
479
- {
480
- "e": 512,
481
- "d": "Record was already modified by another user"
482
- },
483
- {
484
- "e": 513,
485
- "d": "No validation was specified but data cannot fit into the field"
486
- },
487
- {
488
- "e": 600,
489
- "d": "Print error has occurred"
490
- },
491
- {
492
- "e": 601,
493
- "d": "Combined header and footer exceed one page"
494
- },
495
- {
496
- "e": 602,
497
- "d": "Body doesn't fit on a page for current column setup"
498
- },
499
- {
500
- "e": 603,
501
- "d": "Print connection lost"
502
- },
503
- {
504
- "e": 700,
505
- "d": "File is of the wrong file type for import"
506
- },
507
- {
508
- "e": 706,
509
- "d": "EPS file has no preview image"
510
- },
511
- {
512
- "e": 707,
513
- "d": "Graphic translator cannot be found"
514
- },
515
- {
516
- "e": 708,
517
- "d": "Can't import the file, or need color monitor support to import file"
518
- },
519
- {
520
- "e": 711,
521
- "d": "Import translator cannot be found"
522
- },
523
- {
524
- "e": 714,
525
- "d": "Password privileges do not allow the operation"
526
- },
527
- {
528
- "e": 715,
529
- "d": "Specified Excel worksheet or named range is missing"
530
- },
531
- {
532
- "e": 716,
533
- "d": "A SQL query using DELETE, INSERT, or UPDATE is not allowed for ODBC import"
534
- },
535
- {
536
- "e": 717,
537
- "d": "There is not enough XML/XSL information to proceed with the import or export"
538
- },
539
- {
540
- "e": 718,
541
- "d": "Error in parsing XML file (from Xerces)"
542
- },
543
- {
544
- "e": 719,
545
- "d": "Error in transforming XML using XSL (from Xalan)"
546
- },
547
- {
548
- "e": 720,
549
- "d": "Error when exporting; intended format does not support repeating fields"
550
- },
551
- {
552
- "e": 721,
553
- "d": "Unknown error occurred in the parser or the transformer"
554
- },
555
- {
556
- "e": 722,
557
- "d": "Cannot import data into a file that has no fields"
558
- },
559
- {
560
- "e": 723,
561
- "d": "You do not have permission to add records to or modify records in the target table"
562
- },
563
- {
564
- "e": 724,
565
- "d": "You do not have permission to add records to the target table"
566
- },
567
- {
568
- "e": 725,
569
- "d": "You do not have permission to modify records in the target table"
570
- },
571
- {
572
- "e": 726,
573
- "d": "Source file has more records than the target table; not all records were imported"
574
- },
575
- {
576
- "e": 727,
577
- "d": "Target table has more records than the source file; not all records were updated"
578
- },
579
- {
580
- "e": 729,
581
- "d": "Errors occurred during import; records could not be imported"
582
- },
583
- {
584
- "e": 730,
585
- "d": "Unsupported Excel version; convert file to the current Excel format and try again"
586
- },
587
- {
588
- "e": 731,
589
- "d": "File you are importing from contains no data"
590
- },
591
- {
592
- "e": 732,
593
- "d": "This file cannot be inserted because it contains other files"
594
- },
595
- {
596
- "e": 733,
597
- "d": "A table cannot be imported into itself"
598
- },
599
- {
600
- "e": 734,
601
- "d": "This file type cannot be displayed as a picture"
602
- },
603
- {
604
- "e": 735,
605
- "d": "This file type cannot be displayed as a picture; it will be inserted and displayed as a file"
606
- },
607
- {
608
- "e": 736,
609
- "d": "Too much data to export to this format; data will be truncated"
610
- },
611
- {
612
- "e": 738,
613
- "d": "The theme you are importing already exists"
614
- },
615
- {
616
- "e": 800,
617
- "d": "Unable to create file on disk"
618
- },
619
- {
620
- "e": 801,
621
- "d": "Unable to create temporary file on System disk"
622
- },
623
- {
624
- "e": 802,
625
- "d": "Unable to open file"
626
- },
627
- {
628
- "e": 803,
629
- "d": "File is single-user, or host cannot be found"
630
- },
631
- {
632
- "e": 804,
633
- "d": "File cannot be opened as read-only in its current state"
634
- },
635
- {
636
- "e": 805,
637
- "d": "File is damaged; use Recover command"
638
- },
639
- {
640
- "e": 806,
641
- "d": "File cannot be opened with this version of a FileMaker client"
642
- },
643
- {
644
- "e": 807,
645
- "d": "File is not a FileMaker Pro Advanced file or is severely damaged"
646
- },
647
- {
648
- "e": 808,
649
- "d": "Cannot open file because access privileges are damaged"
650
- },
651
- {
652
- "e": 809,
653
- "d": "Disk/volume is full"
654
- },
655
- {
656
- "e": 810,
657
- "d": "Disk/volume is locked"
658
- },
659
- {
660
- "e": 811,
661
- "d": "Temporary file cannot be opened as FileMaker Pro Advanced file"
662
- },
663
- {
664
- "e": 812,
665
- "d": "Exceeded host’s capacity"
666
- },
667
- {
668
- "e": 813,
669
- "d": "Record synchronization error on network"
670
- },
671
- {
672
- "e": 814,
673
- "d": "File(s) cannot be opened because maximum number is open"
674
- },
675
- {
676
- "e": 815,
677
- "d": "Couldn’t open lookup file"
678
- },
679
- {
680
- "e": 816,
681
- "d": "Unable to convert file"
682
- },
683
- {
684
- "e": 817,
685
- "d": "Unable to open file because it does not belong to this solution"
686
- },
687
- {
688
- "e": 819,
689
- "d": "Cannot save a local copy of a remote file"
690
- },
691
- {
692
- "e": 820,
693
- "d": "File is being closed"
694
- },
695
- {
696
- "e": 821,
697
- "d": "Host forced a disconnect"
698
- },
699
- {
700
- "e": 822,
701
- "d": "FileMaker Pro Advanced files not found; reinstall missing files"
702
- },
703
- {
704
- "e": 823,
705
- "d": "Cannot set file to single-user; guests are connected"
706
- },
707
- {
708
- "e": 824,
709
- "d": "File is damaged or not a FileMaker Pro Advanced file"
710
- },
711
- {
712
- "e": 825,
713
- "d": "File is not authorized to reference the protected file"
714
- },
715
- {
716
- "e": 826,
717
- "d": "File path specified is not a valid file path"
718
- },
719
- {
720
- "e": 827,
721
- "d": "File was not created because the source contained no data or is a reference"
722
- },
723
- {
724
- "e": 850,
725
- "d": "Path is not valid for the operating system"
726
- },
727
- {
728
- "e": 851,
729
- "d": "Cannot delete an external file from disk"
730
- },
731
- {
732
- "e": 852,
733
- "d": "Cannot write a file to the external storage"
734
- },
735
- {
736
- "e": 853,
737
- "d": "One or more containers failed to transfer"
738
- },
739
- {
740
- "e": 900,
741
- "d": "General spelling engine error"
742
- },
743
- {
744
- "e": 901,
745
- "d": "Main spelling dictionary not installed"
746
- },
747
- {
748
- "e": 903,
749
- "d": "Command cannot be used in a shared file"
750
- },
751
- {
752
- "e": 905,
753
- "d": "Command requires a field to be active"
754
- },
755
- {
756
- "e": 906,
757
- "d": "Current file is not shared; command can be used only if the file is shared"
758
- },
759
- {
760
- "e": 920,
761
- "d": "Cannot initialize the spelling engine"
762
- },
763
- {
764
- "e": 921,
765
- "d": "User dictionary cannot be loaded for editing"
766
- },
767
- {
768
- "e": 922,
769
- "d": "User dictionary cannot be found"
770
- },
771
- {
772
- "e": 923,
773
- "d": "User dictionary is read-only"
774
- },
775
- {
776
- "e": 951,
777
- "d": "An unexpected error occurred (*)"
778
- },
779
- {
780
- "e": 952,
781
- "d": "Invalid FileMaker Data API token (*)"
782
- },
783
- {
784
- "e": 953,
785
- "d": "Exceeded limit on data the FileMaker Data API can transmit (*)"
786
- },
787
- {
788
- "e": 954,
789
- "d": "Unsupported XML grammar (*)"
790
- },
791
- {
792
- "e": 955,
793
- "d": "No database name (*)"
794
- },
795
- {
796
- "e": 956,
797
- "d": "Maximum number of database sessions exceeded (*)"
798
- },
799
- {
800
- "e": 957,
801
- "d": "Conflicting commands (*)"
802
- },
803
- {
804
- "e": 958,
805
- "d": "Parameter missing (*)"
806
- },
807
- {
808
- "e": 959,
809
- "d": "Custom Web Publishing technology is disabled"
810
- },
811
- {
812
- "e": 960,
813
- "d": "Parameter is invalid"
814
- },
815
- {
816
- "e": 1200,
817
- "d": "Generic calculation error"
818
- },
819
- {
820
- "e": 1201,
821
- "d": "Too few parameters in the function"
822
- },
823
- {
824
- "e": 1202,
825
- "d": "Too many parameters in the function"
826
- },
827
- {
828
- "e": 1203,
829
- "d": "Unexpected end of calculation"
830
- },
831
- {
832
- "e": 1204,
833
- "d": "Number, text constant, field name, or \"(\" expected"
834
- },
835
- {
836
- "e": 1205,
837
- "d": "Comment is not terminated with \"*/\""
838
- },
839
- {
840
- "e": 1206,
841
- "d": "Text constant must end with a quotation mark"
842
- },
843
- {
844
- "e": 1207,
845
- "d": "Unbalanced parenthesis"
846
- },
847
- {
848
- "e": 1208,
849
- "d": "Operator missing, function not found, or \"(\" not expected"
850
- },
851
- {
852
- "e": 1209,
853
- "d": "Name (such as field name or layout name) is missing"
854
- },
855
- {
856
- "e": 1210,
857
- "d": "Plug-in function or script step has already been registered"
858
- },
859
- {
860
- "e": 1211,
861
- "d": "List usage is not allowed in this function"
862
- },
863
- {
864
- "e": 1212,
865
- "d": "An operator (for example, +, -, *) is expected here"
866
- },
867
- {
868
- "e": 1213,
869
- "d": "This variable has already been defined in the Let function"
870
- },
871
- {
872
- "e": 1214,
873
- "d": "Average, Count, Extend, GetRepetition, Max, Min, NPV, StDev, Sum, and GetSummary: expression found where a field alone is needed"
874
- },
875
- {
876
- "e": 1215,
877
- "d": "This parameter is an invalid Get function parameter"
878
- },
879
- {
880
- "e": 1216,
881
- "d": "Only summary fields are allowed as first argument in GetSummary"
882
- },
883
- {
884
- "e": 1217,
885
- "d": "Break field is invalid"
886
- },
887
- {
888
- "e": 1218,
889
- "d": "Cannot evaluate the number"
890
- },
891
- {
892
- "e": 1219,
893
- "d": "A field cannot be used in its own formula"
894
- },
895
- {
896
- "e": 1220,
897
- "d": "Field type must be normal or calculated"
898
- },
899
- {
900
- "e": 1221,
901
- "d": "Data type must be number, date, time, or timestamp"
902
- },
903
- {
904
- "e": 1222,
905
- "d": "Calculation cannot be stored"
906
- },
907
- {
908
- "e": 1223,
909
- "d": "Function referred to is not yet implemented"
910
- },
911
- {
912
- "e": 1224,
913
- "d": "Function referred to does not exist"
914
- },
915
- {
916
- "e": 1225,
917
- "d": "Function referred to is not supported in this context"
918
- },
919
- {
920
- "e": 1300,
921
- "d": "The specified name can't be used"
922
- },
923
- {
924
- "e": 1301,
925
- "d": "A parameter of the imported or pasted function has the same name as a function in the file"
926
- },
927
- {
928
- "e": 1400,
929
- "d": "ODBC client driver initialization failed; make sure ODBC client drivers are properly installed"
930
- },
931
- {
932
- "e": 1401,
933
- "d": "Failed to allocate environment (ODBC)"
934
- },
935
- {
936
- "e": 1402,
937
- "d": "Failed to free environment (ODBC)"
938
- },
939
- {
940
- "e": 1403,
941
- "d": "Failed to disconnect (ODBC)"
942
- },
943
- {
944
- "e": 1404,
945
- "d": "Failed to allocate connection (ODBC)"
946
- },
947
- {
948
- "e": 1405,
949
- "d": "Failed to free connection (ODBC)"
950
- },
951
- {
952
- "e": 1406,
953
- "d": "Failed check for SQL API (ODBC)"
954
- },
955
- {
956
- "e": 1407,
957
- "d": "Failed to allocate statement (ODBC)"
958
- },
959
- {
960
- "e": 1408,
961
- "d": "Extended error (ODBC)"
962
- },
963
- {
964
- "e": 1409,
965
- "d": "Error (ODBC)"
966
- },
967
- {
968
- "e": 1413,
969
- "d": "Failed communication link (ODBC)"
970
- },
971
- {
972
- "e": 1414,
973
- "d": "SQL statement is too long"
974
- },
975
- {
976
- "e": 1450,
977
- "d": "Action requires PHP privilege extension (*)"
978
- },
979
- {
980
- "e": 1451,
981
- "d": "Action requires that current file be remote"
982
- },
983
- {
984
- "e": 1501,
985
- "d": "SMTP authentication failed"
986
- },
987
- {
988
- "e": 1502,
989
- "d": "Connection refused by SMTP server"
990
- },
991
- {
992
- "e": 1503,
993
- "d": "Error with SSL"
994
- },
995
- {
996
- "e": 1504,
997
- "d": "SMTP server requires the connection to be encrypted"
998
- },
999
- {
1000
- "e": 1505,
1001
- "d": "Specified authentication is not supported by SMTP server"
1002
- },
1003
- {
1004
- "e": 1506,
1005
- "d": "Email message(s) could not be sent successfully"
1006
- },
1007
- {
1008
- "e": 1507,
1009
- "d": "Unable to log in to the SMTP server"
1010
- },
1011
- {
1012
- "e": 1550,
1013
- "d": "Cannot load the plug-in, or the plug-in is not a valid plug-in"
1014
- },
1015
- {
1016
- "e": 1551,
1017
- "d": "Cannot install the plug-in; cannot delete an existing plug-in or write to the folder or disk"
1018
- },
1019
- {
1020
- "e": 1552,
1021
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1022
- },
1023
- {
1024
- "e": 1553,
1025
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1026
- },
1027
- {
1028
- "e": 1554,
1029
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1030
- },
1031
- {
1032
- "e": 1555,
1033
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1034
- },
1035
- {
1036
- "e": 1556,
1037
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1038
- },
1039
- {
1040
- "e": 1557,
1041
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1042
- },
1043
- {
1044
- "e": 1558,
1045
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1046
- },
1047
- {
1048
- "e": 1559,
1049
- "d": "Returned by plug-ins; see the documentation that came with the plug-in"
1050
- },
1051
- {
1052
- "e": 1626,
1053
- "d": "Protocol is not supported"
1054
- },
1055
- {
1056
- "e": 1627,
1057
- "d": "Authentication failed"
1058
- },
1059
- {
1060
- "e": 1628,
1061
- "d": "There was an error with SSL"
1062
- },
1063
- {
1064
- "e": 1629,
1065
- "d": "Connection timed out; the timeout value is 60 seconds"
1066
- },
1067
- {
1068
- "e": 1630,
1069
- "d": "URL format is incorrect"
1070
- },
1071
- {
1072
- "e": 1631,
1073
- "d": "Connection failed"
1074
- },
1075
- {
1076
- "e": 1632,
1077
- "d": "The certificate has expired"
1078
- },
1079
- {
1080
- "e": 1633,
1081
- "d": "The certificate is self-signed"
1082
- },
1083
- {
1084
- "e": 1634,
1085
- "d": "A certificate verification error occurred"
1086
- },
1087
- {
1088
- "e": 1635,
1089
- "d": "Connection is unencrypted"
1090
- }
1091
- ]
1092
-
1093
- interface Script {
1094
- name: string
1095
- parameter: string
1096
- }
1097
-
1098
- interface recordObject {
1099
- recordId: number
1100
- modId: number,
1101
- fieldData: any,
1102
- portalData?: portalDataObject
1103
- }
1104
-
1105
- interface fileMakerResponse {
1106
- response: any,
1107
- messages: any[]
1108
- }
1109
-
1110
- interface portalDataObject {
1111
- [key: string]: recordObject
1112
- }
1113
-
1114
- interface FieldMetaData {
1115
- name: string,
1116
- type: string,
1117
- displayType: string,
1118
- result: string,
1119
- global: boolean,
1120
- autoEnter: boolean,
1121
- fourDigitYear: boolean,
1122
- maxRepeat: number,
1123
- maxCharacters: number,
1124
- notEmpty: boolean,
1125
- numeric: boolean,
1126
- timeOfDay: false,
1127
- repetitionStart: number,
1128
- repetitionEnd: number
1129
- }
1130
-
1131
- interface LayoutMetaData {
1132
- fieldMetaData: FieldMetaData[],
1133
- portalMetaData?: {
1134
- [key: string]: FieldMetaData[]
1135
- }
1136
- }
1137
-
1138
- interface FMHostMetadata {
1139
- productInfo: {
1140
- buildDate: Date,
1141
- name: string,
1142
- version: string,
1143
- dateFormat: string,
1144
- timeFormat: string,
1145
- timeStampFormat: string
1146
- }
1147
- }
1148
-
1149
- export interface ScriptResult {
1150
- scriptError?: FMError,
1151
- scriptResult?: string
1152
- }
1153
-
1154
- export interface ContainerBufferResult {
1155
- buffer: Buffer,
1156
- mime: string,
1157
- request: http.IncomingMessage
1158
- }
1159
- export default class FMHost {
1160
- readonly hostname: string
1161
- readonly timezoneOffset: number
1162
- readonly verify: boolean
1163
- metadata: FMHostMetadata
1164
-
1165
- constructor(_hostname: string, timezoneOffset = 0 - (new Date()).getTimezoneOffset(), verify = true) {
1166
- if (!(/^https?:\/\//).test(_hostname)) throw "hostname MUST begin with either http:// or https://"
1167
- this.hostname = _hostname
1168
- this.timezoneOffset = timezoneOffset
1169
- this.verify = verify
1170
- }
1171
-
1172
- async listDatabases(credentials?: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris) {
1173
- let headers = {}
1174
- if (credentials) {
1175
- headers = generateAuthorizationHeaders(credentials)
1176
- }
1177
-
1178
- let _fetch = await fetch(`${this.hostname}/fmi/data/v2/databases`, {
1179
- method: "GET",
1180
- headers
1181
- })
1182
- let data = await _fetch.json() as fileMakerResponse
1183
- // console.log(data.messages[0])
1184
-
1185
- if (data.messages[0].code === "0") {
1186
- return data.response.databases
1187
- }
1188
- else {
1189
- // @ts-ignore
1190
- throw new FMError(data.messages[0].code, data.status, data)
1191
- }
1192
- }
1193
-
1194
- database(data: databaseOptionsWithExternalSources) {
1195
- return new Database(this, data)
1196
- }
1197
-
1198
- async getMetadata() {
1199
- if (this.metadata) return this.metadata
1200
-
1201
- let _fetch = await fetch(`${this.hostname}/fmi/data/v2/productInfo`, {
1202
- method: "GET",
1203
- })
1204
- let data = await _fetch.json() as fileMakerResponse
1205
- // console.log(data.messages[0])
1206
-
1207
- if (data.messages[0].code === "0") {
1208
- this.metadata = data.response
1209
- return data.response
1210
- }
1211
- else {
1212
- // @ts-ignore
1213
- throw new FMError(data.messages[0].code, data.status, data)
1214
- }
1215
- }
1216
- }
1217
-
1218
- export class Database extends EventEmitter {
1219
- private _token: any;
1220
- readonly host: FMHost;
1221
- private connection_details: databaseOptionsWithExternalSources
1222
- private cookies: { [key: string]: string } = {}
1223
- readonly name: string;
1224
-
1225
- constructor(host: FMHost, conn: databaseOptionsWithExternalSources) {
1226
- super()
1227
- this.host = host
1228
- this.name = conn.database
1229
- this.connection_details = conn
1230
- }
1231
-
1232
- private generateExternalSourceLogin(data: databaseOptionsBase) {
1233
- if (data.credentials.method === "filemaker") {
1234
- let _data = <loginOptionsFileMaker>data.credentials
1235
- return {
1236
- database: data.database,
1237
- username: _data.username,
1238
- password: _data.password
1239
- }
1240
- }
1241
- else {
1242
- throw "Not yet supported login method"
1243
- }
1244
- }
1245
-
1246
- logout(): Promise<void> {
1247
- return new Promise(async (resolve, reject) => {
1248
- if (!this.token) reject(new Error("Not logged in"))
1249
-
1250
- let _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
1251
- method: "DELETE",
1252
- headers: {
1253
- "content-type": "application/json"
1254
- }
1255
- })
1256
- let data = await _fetch.json()
1257
- // console.log(data)
1258
- this._token = null
1259
- resolve()
1260
- })
1261
- }
1262
-
1263
- login() {
1264
- return new Promise<string>(async (resolve, reject) => {
1265
- // Reset cookies
1266
- this.cookies = {}
1267
-
1268
- try {
1269
- await this.host.getMetadata()
1270
- } catch (e) {
1271
- reject(e)
1272
- return
1273
- }
1274
- if (this.token) throw new Error("Already logged in. Run logout() first")
1275
-
1276
- if (this.connection_details.credentials.method === "token") {
1277
- this._token = (<loginOptionsToken>this.connection_details.credentials).token
1278
- resolve(this.token)
1279
- }
1280
- else {
1281
- fetch(`${this.endpoint}/sessions`, {
1282
- hostname: this.host.hostname,
1283
- port: 443,
1284
- method: "POST",
1285
- headers: generateAuthorizationHeaders(this.connection_details.credentials) as unknown as HeadersInit,
1286
- body: JSON.stringify({
1287
- fmDataSource: this.connection_details.externalSources.map(i => {
1288
- let _i = <databaseOptionsBase>i
1289
- return this.generateExternalSourceLogin(_i)
1290
- })
1291
- })
1292
- }).then(async res => {
1293
- let _res = <any>(await res.json())
1294
- if (res.status === 200) {
1295
- this._token = res.headers.get('x-fm-data-access-token')
1296
- resolve(this._token)
1297
- }
1298
- else {
1299
- reject(new FMError(_res.messages[0].code, _res.status, res))
1300
- }
1301
- })
1302
- .catch(e => {
1303
- reject(e)
1304
- })
1305
- }
1306
- })
1307
- }
1308
-
1309
- get token() {
1310
- return this._token
1311
- }
1312
-
1313
- get endpoint(): string {
1314
- return `${this.host.hostname}/fmi/data/v2/databases/${this.name}`
1315
- }
1316
-
1317
- async apiRequest(url: string | Request, options: any = {}, autoRelogin = true): Promise<any> {
1318
- if (!options.headers) options.headers = {}
1319
- options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json"
1320
- options.headers["authorization"] = "Bearer " + this._token
1321
- options.rejectUnauthorized = this.host.verify
1322
-
1323
- let _fetch = await fetch(url, options)
1324
- if (_fetch.headers.get('set-cookie')) {
1325
- for (let cookie of _fetch.headers.get('set-cookie')) {
1326
- let cookie_split = cookie.split("=")
1327
- this.cookies[cookie_split[0]] = cookie_split[1]
1328
- }
1329
- }
1330
- let data = await _fetch.json() as fileMakerResponse
1331
- // console.log(data.messages[0])
1332
- if (data.messages[0].code == 952 && autoRelogin) {
1333
- this._token = null
1334
- await this.login()
1335
- return await this.apiRequest(url, options, false)
1336
- }
1337
- return (data as any)
1338
- }
1339
-
1340
- getLayout(name): Layout {
1341
- return new Layout(this, name)
1342
- }
1343
-
1344
- setGlobals(globalFields): Promise<void> {
1345
- // console.log({globalFields})
1346
- return new Promise((resolve, reject) => {
1347
- this.apiRequest(`${this.endpoint}/globals`, {
1348
- method: "PATCH",
1349
- body: JSON.stringify({globalFields})
1350
- }).then(res => {
1351
- // console.log(res)
1352
- if (res.messages[0].code === "0") {
1353
- resolve()
1354
- }
1355
- else {
1356
- reject(
1357
- (res.messages[0].code, res.status, res))
1358
- }
1359
- })
1360
- .catch(e => {
1361
- reject(e)
1362
- })
1363
- })
1364
- }
1365
-
1366
- script(name, parameter = ""): Script {
1367
- return ({name, parameter} as Script)
1368
- }
1369
-
1370
- _tokenExpired() {
1371
- this.emit("token_expired")
1372
- }
1373
-
1374
- streamContainer(field, url): Promise<http.IncomingMessage> {
1375
- return new Promise((resolve, reject) => {
1376
- if (field.metadata.result !== "container") {
1377
- reject("Cannot stream the field " + field.id + " as it is not a container")
1378
- return
1379
- }
1380
- if (!url || typeof url !== "string") {
1381
- reject("Container is empty, or has invalid value")
1382
- return
1383
- }
1384
-
1385
- let headers = {}
1386
- if (Object.keys(this.cookies).length !== 0) {
1387
- headers["Cookie"] = Object.keys(this.cookies)
1388
- .map(key => {
1389
- return key + "=" + this.cookies[key]
1390
- })
1391
- .join("; ")
1392
- }
1393
-
1394
- // Automatically switch between the http and https modules, based on which is needed
1395
- (url.startsWith("https") ? https : http).get(url, {
1396
- headers
1397
- }, (res) => {
1398
- // Check for the 'set-cookie' header. If it exists, remember it and strip it for better security.
1399
- if (res.headers['set-cookie']) {
1400
- for (let cookie of res.headers['set-cookie']) {
1401
- let cookie_split = cookie.split("=")
1402
- this.cookies[cookie_split[0]] = cookie_split[1]
1403
- }
1404
- res.headers['set-cookie'] = null
1405
- }
1406
-
1407
- resolve(res)
1408
- })
1409
- })
1410
- }
1411
- }
1412
-
1413
- export class Layout {
1414
- readonly database: Database;
1415
- readonly records = new LayoutRecordManager(this)
1416
- protected name: string;
1417
- metadata: any;
1418
-
1419
- constructor(database: Database, name: string) {
1420
- this.database = database
1421
- this.name = name
1422
- }
1423
-
1424
- get endpoint() {
1425
- return `${this.database.endpoint}/layouts/${this.name}`
1426
- }
1427
-
1428
- /**
1429
- * @deprecated use layout.records.create() instead
1430
- */
1431
- createRecord(): Promise<LayoutRecord> {
1432
- return this.records.create()
1433
- }
1434
-
1435
- /**
1436
- * @deprecated use layout.records.get() instead
1437
- */
1438
- getRecord(recordId): Promise<LayoutRecord> {
1439
- return this.records.get(recordId)
1440
- }
1441
-
1442
- /**
1443
- * @deprecated use layout.records.find() instead
1444
- */
1445
- newFind(): Find {
1446
- return this.records.find()
1447
- }
1448
-
1449
- runScript(script): Promise<ScriptResult> {
1450
- let trace = new Error()
1451
- return new Promise(async (resolve, reject) => {
1452
- let url = `${this.endpoint}/script/${encodeURIComponent(script.name)}`
1453
- if (script.parameter) url += "?script.param=" + encodeURIComponent(script.parameter)
1454
- this.database.apiRequest(url, {
1455
- port: 443,
1456
- method: "GET"
1457
- })
1458
- .then(res => {
1459
- if (res.messages[0].code === "0") {
1460
- let error = parseInt(res.response.scriptError)
1461
- let msg: ScriptResult = {
1462
- scriptError: error ? new FMError(error, 200, res, trace) : undefined,
1463
- scriptResult: res.response.scriptResult
1464
- }
1465
- resolve(msg)
1466
- }
1467
- else {
1468
- // console.log(res)
1469
- reject(new FMError(res.messages[0].code, res.status, res, trace))
1470
- }
1471
- })
1472
- .catch(e => {
1473
- reject(e)
1474
- })
1475
- })
1476
- }
1477
-
1478
- public getLayoutMeta(): Promise<Layout | FMError> {
1479
- return new Promise((resolve, reject) => {
1480
- if (this.metadata) {
1481
- resolve(this.metadata)
1482
- return
1483
- }
1484
-
1485
- this.database.apiRequest(this.endpoint).then(res => {
1486
- this.metadata = res.response
1487
- resolve(this)
1488
- })
1489
- .catch(e => reject(e))
1490
- })
1491
- }
1492
- }
1493
-
1494
- class LayoutRecordManager {
1495
- readonly layout: Layout
1496
-
1497
- constructor(layout: Layout) {
1498
- this.layout = layout
1499
- }
1500
-
1501
- create(): Promise<LayoutRecord> {
1502
- return new Promise((resolve, reject) => {
1503
- // Get the layout's metadata
1504
- this.layout.getLayoutMeta().then(layout => {
1505
- let fields = {}
1506
- for (let _field of this.layout.metadata.fieldMetaData) {
1507
- fields[_field.name] = ""
1508
- }
1509
- let portals = {}
1510
- for (let _portal of Object.keys(this.layout.metadata.portalMetaData)) portals[_portal] = []
1511
- resolve(new LayoutRecord(this.layout, -1, 0, fields, portals))
1512
- }).catch(e => {
1513
- reject(e)
1514
- })
1515
- })
1516
- }
1517
-
1518
- get(recordId): Promise<LayoutRecord> {
1519
- return new Promise((resolve, reject) => {
1520
- let record
1521
- this.layout.getLayoutMeta()
1522
- .then(layout => {
1523
- record = new LayoutRecord(this.layout, recordId)
1524
- return record.get()
1525
- })
1526
- .then(() => {
1527
- resolve(record)
1528
- })
1529
- .catch(e => {
1530
- reject(e)
1531
- })
1532
- })
1533
- }
1534
-
1535
- range(start = 0, limit = 100) {
1536
- return new RecordGetRange(this.layout, start, limit)
1537
- }
1538
-
1539
- find(start = 0, limit = 100): Find {
1540
- return new Find(this.layout, start, limit)
1541
- }
1542
- }
1543
-
1544
- export class RecordBase extends EventEmitter {
1545
- readonly layout: Layout;
1546
- public recordId: number;
1547
- modId: number;
1548
- fields: Field[];
1549
- portals: Portal[] = [];
1550
- protected portalData: any[];
1551
-
1552
- constructor(layout, recordId, modId = recordId) {
1553
- super();
1554
- this.layout = layout
1555
- this.recordId = recordId
1556
- this.modId = modId
1557
- }
1558
-
1559
- get endpoint(): string {
1560
- return `${this.layout.endpoint}/records/${this.recordId}`
1561
- }
1562
-
1563
- get edited(): boolean {
1564
- return !!this.fields.find(i => i.edited)
1565
- }
1566
-
1567
- processFieldData(fieldData): Field[] {
1568
- return Object.keys(fieldData).map(item => {
1569
- let _field = new Field(this, item, fieldData[item])
1570
- if (!!fieldData[item]) {
1571
- if (_field.metadata.result === "timeStamp") {
1572
- // @ts-ignore
1573
- let date = moment.default(fieldData[item], this.layout.database.host.metadata.productInfo.timeStampFormat.replace("dd", "DD"))
1574
- .utcOffset(this.layout.database.host.timezoneOffset, true)
1575
- .local()
1576
- _field.set(date.toDate())
1577
- _field.edited = false
1578
-
1579
- }
1580
- else if (_field.metadata.result === "time") {
1581
- // @ts-ignore
1582
- let date = moment.default(fieldData[item], this.layout.database.host.metadata.productInfo.timeFormat.replace("dd", "DD"))
1583
- .utcOffset(this.layout.database.host.timezoneOffset, true)
1584
- .local()
1585
- _field.set(date.toDate())
1586
- _field.edited = false
1587
- }
1588
- else if (_field.metadata.result === "date") {
1589
- // @ts-ignore
1590
- let date = moment.default(fieldData[item], this.layout.database.host.metadata.productInfo.dateFormat.replace("dd", "DD"))
1591
- .utcOffset(this.layout.database.host.timezoneOffset, true)
1592
- .local()
1593
- _field.set(date.toDate())
1594
- _field.edited = false
1595
- }
1596
- }
1597
- return _field
1598
- })
1599
- }
1600
-
1601
- _onSave() {
1602
- this.emit("saved")
1603
- for (let field of this.fields) field.edited = false
1604
- }
1605
-
1606
- getField(field) {
1607
- return this.fields.find(_field => _field.id === field)
1608
- }
1609
-
1610
- toObject(filter = (a) => a.edited,
1611
- portalFilter = (a) => a.records.find(record => record.edited),
1612
- portalRowFilter = (a) => a.edited,
1613
- portalFieldFilter = (a) => a.edited
1614
- ): recordObject {
1615
- let fields_processed = {}
1616
- for (let field of this.fields.filter(field => filter(field))) {
1617
- let value = field.value
1618
- if (value instanceof Date) {
1619
- // @ts-ignore
1620
- let _value = moment.default(value)
1621
- .utcOffset(this.layout.database.host.timezoneOffset)
1622
-
1623
- // @ts-ignore
1624
-
1625
- switch (field.metadata.result) {
1626
- case "time":
1627
- value = _value.format(this.layout.database.host.metadata.productInfo.timeFormat.replace("dd", "DD"))
1628
- break
1629
- case "date":
1630
- value = _value.format(this.layout.database.host.metadata.productInfo.dateFormat.replace("dd", "DD"))
1631
- break
1632
- default:
1633
- value = _value.format(this.layout.database.host.metadata.productInfo.timeStampFormat.replace("dd", "DD"))
1634
- }
1635
- }
1636
- fields_processed[field.id] = value
1637
- }
1638
- let obj = {
1639
- "recordId": this.recordId,
1640
- "modId": this.modId,
1641
- "fieldData": fields_processed
1642
- }
1643
-
1644
- // Check if there's been any edited portal information
1645
- let portals = this.portals.filter(a => portalFilter(a))
1646
- if (portals) {
1647
- obj["portalData"] = {}
1648
- for (let portal of portals) {
1649
- obj["portalData"][portal.name] = portal.records.filter(a => portalRowFilter(a)).map(record => {
1650
- return record.toObject(portalFieldFilter)
1651
- })
1652
- }
1653
- }
1654
- return obj
1655
- }
1656
- }
1657
-
1658
- export class LayoutRecord extends RecordBase {
1659
- portals: Portal[];
1660
-
1661
- constructor(layout, recordId, modId = recordId, fieldData = {}, portalData = null) {
1662
- super(layout, recordId, modId);
1663
- this.fields = this.processFieldData(fieldData)
1664
- this.portals = []
1665
- if (portalData) {
1666
- this.processPortalData(portalData)
1667
- }
1668
- }
1669
-
1670
- commit(extraBody: extraBodyOptions = {}): Promise<this> {
1671
- let trace = new Error()
1672
- return new Promise(async (resolve, reject) => {
1673
- let data = this.toObject()
1674
- delete data.recordId
1675
- delete data.modId
1676
-
1677
- if (extraBody.scripts?.after) {
1678
- data["script"] = extraBody.scripts.after.name
1679
- if (extraBody.scripts.after.parameter) data["script.param"] = extraBody.scripts.after.parameter
1680
- }
1681
- if (extraBody.scripts?.prerequest) {
1682
- data["script.prerequest"] = extraBody.scripts.after.name
1683
- if (extraBody.scripts.prerequest.parameter) data["script.prerequest.param"] = extraBody.scripts.prerequest.parameter
1684
- }
1685
- if (extraBody.scripts?.presort) {
1686
- data["script.presort"] = extraBody.scripts.presort.name
1687
- if (extraBody.scripts.presort.parameter) data["script.presort.param"] = extraBody.scripts.presort.parameter
1688
- }
1689
-
1690
- if (this.recordId === -1) {
1691
- // This is a new LayoutRecord
1692
-
1693
- this.layout.database.apiRequest(`${this.layout.endpoint}/records`, {
1694
- port: 443,
1695
- method: "POST",
1696
- body: JSON.stringify(data)
1697
- })
1698
- .then(res => {
1699
- if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
1700
- reject(new FMError(res.response.scriptError, res.status, res, trace))
1701
- }
1702
- else if (res.messages[0].code === "0") {
1703
- this.recordId = parseInt(res.response.recordId)
1704
- this.modId = parseInt(res.response.modId)
1705
- resolve(this)
1706
- }
1707
- else {
1708
- reject(new FMError(res.messages[0].code, res.status, res, trace))
1709
- }
1710
- })
1711
- .catch(e => {
1712
- reject(e)
1713
- })
1714
-
1715
- return
1716
- }
1717
-
1718
- // for (let item of Object.keys(data)) extraBody[item] = data[item]
1719
- this.layout.database.apiRequest(this.endpoint, {
1720
- port: 443,
1721
- method: "PATCH",
1722
- body: JSON.stringify(data)
1723
- })
1724
- .then(res => {
1725
- if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
1726
- reject(new FMError(res.response.scriptError, res.status, res, trace))
1727
- }
1728
- else if (res.messages[0].code === "0") {
1729
- this.modId = res.response.modId
1730
- this._onSave()
1731
- resolve(this)
1732
- }
1733
- else {
1734
- reject(new FMError(res.messages[0].code, res.status, res, trace))
1735
- }
1736
- })
1737
- .catch(e => {
1738
- reject(e)
1739
- })
1740
- })
1741
- }
1742
-
1743
- protected processPortalData(portalData): void {
1744
- this.portals = []
1745
- for (let item of Object.keys(portalData)) {
1746
- let _portal = new Portal(this, item)
1747
- _portal.records = portalData[item].map(item => {
1748
- let fieldData = item;
1749
- delete fieldData.recordId;
1750
- delete fieldData.modId;
1751
- return new PortalRecord(this, _portal, item.recordId, item.modId, fieldData);
1752
- })
1753
- this.portals.push(_portal);
1754
- }
1755
- }
1756
-
1757
- get(): Promise<this> {
1758
- let trace = new Error()
1759
- if (this.recordId === -1) {
1760
- throw "Cannot get this RecordBase until a commit() is done."
1761
- }
1762
- return new Promise(async (resolve, reject) => {
1763
- if (!this.layout.metadata) await this.layout.getLayoutMeta()
1764
-
1765
- this.layout.database.apiRequest(this.endpoint, {
1766
- port: 443,
1767
- method: "GET"
1768
- })
1769
- .then(res => {
1770
- if (res.messages[0].code === "0") {
1771
- // console.log(res, res.response.data)
1772
- this.modId = res.response.data[0].modId
1773
- this.fields = this.processFieldData(res.response.data[0].fieldData)
1774
- this.portalData = []
1775
- if (res.response.data[0].portalData) this.processPortalData(res.response.data[0].portalData)
1776
- resolve(this)
1777
- }
1778
- else {
1779
- reject(new FMError(res.messages[0].code, res.status, res, trace))
1780
- }
1781
- })
1782
- .catch(e => {
1783
- reject(e)
1784
- })
1785
- })
1786
- }
1787
-
1788
- getPortal(portal) {
1789
- return this.portals.find(p => p.name === portal)
1790
- }
1791
-
1792
- duplicate(): Promise<LayoutRecord> {
1793
- let trace = new Error()
1794
- return new Promise(async (resolve, reject) => {
1795
- this.layout.database.apiRequest(this.endpoint, {
1796
- port: 443,
1797
- method: "POST"
1798
- })
1799
- .then(res => {
1800
- if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
1801
- reject(new FMError(res.response.scriptError, res.status, res, trace))
1802
- }
1803
- else if (res.messages[0].code === "0") {
1804
- let data = this.toObject((a) => true, (a) => true, (a) => false, (a) => false)
1805
- let _res = new LayoutRecord(this.layout, res.response.recordId, res.response.modId, data.fieldData, data.portalData)
1806
-
1807
- this.emit("duplicated")
1808
- resolve(_res)
1809
- }
1810
- else {
1811
- reject(new FMError(res.messages[0].code, res.status, res, trace))
1812
- }
1813
- })
1814
- .catch(e => {
1815
- reject(e)
1816
- })
1817
- })
1818
- }
1819
-
1820
- delete(): Promise<void> {
1821
- let trace = new Error()
1822
- return new Promise(async (resolve, reject) => {
1823
- this.layout.database.apiRequest(this.endpoint, {
1824
- port: 443,
1825
- method: "DELETE"
1826
- })
1827
- .then(res => {
1828
- if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
1829
- reject(new FMError(res.response.scriptError, res.status, res, trace))
1830
- }
1831
- else if (res.messages[0].code === "0") {
1832
- this.emit("deleted")
1833
- resolve()
1834
- }
1835
- else {
1836
- reject(new FMError(res.messages[0].code, res.status, res, trace))
1837
- }
1838
- })
1839
- .catch(e => {
1840
- reject(e)
1841
- })
1842
- })
1843
- }
1844
- }
1845
-
1846
- export class Field {
1847
- record: RecordBase;
1848
- id: string;
1849
- protected _value: string | number | Date;
1850
- edited: boolean;
1851
-
1852
- constructor(record, id, contents) {
1853
- this.record = record
1854
- this.id = id
1855
- this._value = contents
1856
- this.edited = false
1857
- }
1858
-
1859
- set(content: string | number | Date) {
1860
- if (this.metadata.result === "container") throw "Cannot set container value using set(). Use upload() instead."
1861
- if (
1862
- (this.metadata.result === "timeStamp" ||
1863
- this.metadata.result === "date" ||
1864
- this.metadata.result === "time")
1865
- && !(content instanceof Date) && !!content
1866
- ) {
1867
- throw "Value was not an instance of Date: " + content
1868
- }
1869
- this._value = content
1870
- this.edited = true
1871
- }
1872
-
1873
- get metadata(): FieldMetaData {
1874
- if (!this.record.layout.metadata) {
1875
- // Default to a regular text field
1876
- return {
1877
- name: this.id.toString(),
1878
- type: 'normal',
1879
- displayType: 'editText',
1880
- result: 'text',
1881
- global: false,
1882
- autoEnter: true,
1883
- fourDigitYear: false,
1884
- maxRepeat: 1,
1885
- maxCharacters: 0,
1886
- notEmpty: false,
1887
- numeric: false,
1888
- timeOfDay: false,
1889
- repetitionStart: 1,
1890
- repetitionEnd: 1
1891
- } as FieldMetaData
1892
- }
1893
- if (this.record instanceof PortalRecord) {
1894
- return this.record.layout.metadata.portalMetaData[this.record.portal.name || "portal not attached"].find(i => i.name === this.id) as FieldMetaData || {
1895
- name: this.id.toString(),
1896
- type: 'normal',
1897
- displayType: 'editText',
1898
- result: 'text',
1899
- global: false,
1900
- autoEnter: true,
1901
- fourDigitYear: false,
1902
- maxRepeat: 1,
1903
- maxCharacters: 0,
1904
- notEmpty: false,
1905
- numeric: false,
1906
- timeOfDay: false,
1907
- repetitionStart: 1,
1908
- repetitionEnd: 1
1909
- } as FieldMetaData
1910
- }
1911
- else {
1912
- return this.record.layout.metadata.fieldMetaData.find(i => i.name === this.id) as FieldMetaData || {
1913
- name: this.id.toString(),
1914
- type: 'normal',
1915
- displayType: 'editText',
1916
- result: 'text',
1917
- global: false,
1918
- autoEnter: true,
1919
- fourDigitYear: false,
1920
- maxRepeat: 1,
1921
- maxCharacters: 0,
1922
- notEmpty: false,
1923
- numeric: false,
1924
- timeOfDay: false,
1925
- repetitionStart: 1,
1926
- repetitionEnd: 1
1927
- } as FieldMetaData
1928
- }
1929
- }
1930
-
1931
- get value() {
1932
- // if (this.metadata.result === "container") throw "Use await field.stream() to get the contents of a container field, instead of field.value"
1933
- return this._value
1934
- }
1935
-
1936
- get string(): string {
1937
- if (typeof this._value === "string") {
1938
- return this._value
1939
- }
1940
- throw "Field value is not a string"
1941
- }
1942
-
1943
- get date(): Date {
1944
- if (this._value instanceof Date) {
1945
- return this._value
1946
- }
1947
- throw "Field value is not a date"
1948
- }
1949
-
1950
- get number(): number {
1951
- if (typeof this._value === "number") {
1952
- return this._value
1953
- }
1954
- throw "Field value is not a number"
1955
- }
1956
-
1957
- upload(buffer: Buffer, filename: string, mime: string): Promise<void> {
1958
- let trace = new Error()
1959
- if (this.metadata.result !== "container") throw "Cannot upload a file to the field; " + this.id + " (not a container field)"
1960
- return new Promise(async (resolve, reject) => {
1961
- let form = new FormData()
1962
- form.append("upload", new File([buffer], filename, {type: mime}))
1963
-
1964
- let _fetch = await fetch(`${this.record.endpoint}/containers/${this.id}/1`, {
1965
- method: "POST",
1966
- headers: {"Authorization": "Bearer " + this.record.layout.database.token},
1967
- body: form
1968
- }).then(res => res.json())
1969
- .then(data => {
1970
- let _res = data as any
1971
- if (_res.messages[0].code === "0") {
1972
- resolve()
1973
- }
1974
- else {
1975
- reject(new FMError(_res.messages[0].code, _res.status, data, trace))
1976
- }
1977
- })
1978
- .catch(e => {
1979
- reject(e)
1980
- })
1981
- })
1982
- }
1983
-
1984
- download(mode: DOWNLOAD_MODES.Stream): Promise<http.IncomingMessage>
1985
- download(mode: DOWNLOAD_MODES.Buffer): Promise<ContainerBufferResult>
1986
- download(mode: DOWNLOAD_MODES = DOWNLOAD_MODES.Stream): Promise<http.IncomingMessage | ContainerBufferResult> {
1987
- return new Promise((resolve, reject) => {
1988
- this.record.layout.database.streamContainer(this, this._value)
1989
- .then(stream => {
1990
- if (mode === DOWNLOAD_MODES.Stream) {
1991
- resolve(stream)
1992
- return
1993
- }
1994
-
1995
- let body = []
1996
- stream.on("data", chunk => {
1997
- body.push(chunk)
1998
- })
1999
- stream.on("error", (e) => {
2000
- reject(e)
2001
- })
2002
- stream.on("end", () => {
2003
- resolve({
2004
- buffer: Buffer.concat(body),
2005
- mime: stream.headers["content-type"],
2006
- request: stream
2007
- } as ContainerBufferResult)
2008
- })
2009
- })
2010
- .catch(e => {
2011
- reject(e)
2012
- })
2013
- })
2014
- }
2015
- }
2016
-
2017
- export class Portal {
2018
- readonly record: LayoutRecord;
2019
- readonly name: string;
2020
- public records: PortalRecord[];
2021
-
2022
- constructor(record: LayoutRecord, name: string) {
2023
- this.record = record
2024
- this.name = name
2025
- }
2026
-
2027
- create() {
2028
- let fields = {}
2029
- for (let _field of this.record.layout.metadata.portalMetaData[this.name]) {
2030
- fields[_field.name] = ""
2031
- }
2032
- let record = new PortalRecord(this.record, this, -1, -1, fields)
2033
- this.records.push(record)
2034
- return record
2035
- }
2036
- }
2037
-
2038
- export class PortalRecord extends RecordBase {
2039
- readonly portal: Portal;
2040
-
2041
- constructor(record, portal, recordId, modId = recordId, fieldData = {}) {
2042
- super(record.layout, recordId, modId);
2043
-
2044
- this.portal = portal
2045
- this.fields = this.processFieldData(fieldData)
2046
- }
2047
-
2048
- _onSave() {
2049
- super._onSave();
2050
- this.portal.record._onSave()
2051
- }
2052
-
2053
- commit(extraBody: extraBodyOptions = {}) {
2054
- return this.portal.record.commit(extraBody)
2055
- }
2056
-
2057
- toObject(fieldFilter): any {
2058
- super.toObject()
2059
- let res = {
2060
- recordId: this.recordId === -1 ? undefined : this.recordId, modId: this.modId === - 1 ? undefined : this.modId
2061
- } as recordObject
2062
- for (let field of this.fields.filter(a => fieldFilter(a))) res[field.id] = field.value
2063
- // console.log(res)
2064
- return res
2065
- }
2066
-
2067
- getField(field) {
2068
- return this.fields.find(_field => _field.id === field) || this.fields.find(_field => _field.id === this.portal.name + "::" + field)
2069
- }
2070
- }
2071
-
2072
- export class RecordGetOperation {
2073
- protected layout: Layout
2074
- protected limit: number = 100
2075
- protected scripts: object
2076
- protected sort: object[]
2077
- protected limitPortals: limitPortalsInterface[] = []
2078
- protected offset: number = 0
2079
-
2080
- constructor(layout) {
2081
- this.layout = layout
2082
- this.scripts = {
2083
- "script": null, // Runs after everything
2084
- "script.prerequeset": null, // Runs before the request
2085
- "script.presort": null // Runs before the sort
2086
- }
2087
- this.sort = []
2088
- }
2089
-
2090
- /*
2091
- addToPortalLimit() will adjust the results of the get request so that if a layout has multiple portals in it,
2092
- only data from the specified ones will be read. This may help reduce load on your FileMaker API
2093
- */
2094
- addToPortalLimit(portal: Portal, offset = 0, limit = 100) {
2095
- if (offset < 0) throw "Portal offset cannot be less than 0"
2096
- this.limitPortals.push({portal, offset, limit})
2097
- return this
2098
- }
2099
-
2100
- setLimit(limit: number) {
2101
- if (limit < 1) throw "Record limit too low"
2102
- this.limit = limit
2103
- return this
2104
- }
2105
-
2106
- addSort(fieldName, sortOrder) {
2107
- this.sort.push({fieldName, sortOrder})
2108
- return this
2109
- }
2110
-
2111
- setOffset(offset: number) {
2112
- if (offset < 0) throw "Record offset too low"
2113
- this.limit = offset
2114
- return this
2115
- }
2116
- }
2117
-
2118
- export class RecordGetRange extends RecordGetOperation {
2119
- constructor(layout, start = 0, limit = 100) {
2120
- super(layout)
2121
- this.setOffset(start)
2122
- this.setLimit(limit)
2123
- }
2124
-
2125
- private generateQueryParams(extraBody: extraBodyOptions = {}) {
2126
- let params = []
2127
- if (this.limit !== 100) params.push("_limit=" + this.limit)
2128
- if (this.offset !== 0) params.push("_offset=" + this.offset)
2129
- if (this.sort.length > 0) params.push("_sort=" + encodeURI(JSON.stringify(this.sort)))
2130
- if (this.limitPortals.length > 0) {
2131
- params.push("portal=" + encodeURI(JSON.stringify(this.limitPortals.map(p => p.portal.name))))
2132
- for (let item of this.limitPortals) {
2133
- if (item.offset !== 0) params.push("_offset." + item.portal.name.replace(/[^0-9A-z]/g, "") + "=" + item.offset)
2134
- if (item.limit !== 100) params.push("_limit." + item.portal.name.replace(/[^0-9A-z]/g, "") + "=" + item.limit)
2135
- }
2136
- }
2137
- if (extraBody.scripts) {
2138
- if (extraBody.scripts.prerequest) {
2139
- params.push("script.prerequest=" + extraBody.scripts.prerequest.name)
2140
- if (extraBody.scripts.prerequest.parameter) params.push("script.prerequest.param=" + extraBody.scripts.prerequest.parameter)
2141
- }
2142
- if (extraBody.scripts.presort) {
2143
- params.push("script.presort=" + extraBody.scripts.presort.name)
2144
- if (extraBody.scripts.presort.parameter) params.push("script.presort.param=" + extraBody.scripts.presort.parameter)
2145
- }
2146
- if (extraBody.scripts.after) {
2147
- params.push("script=" + extraBody.scripts.after.name)
2148
- if (extraBody.scripts.after.parameter) params.push("script.param=" + extraBody.scripts.after.parameter)
2149
- }
2150
- }
2151
-
2152
- if (params.length === 0) return ""
2153
- return "?" + params.join("&")
2154
- }
2155
-
2156
- /**
2157
- * @deprecated in favour of .fetch()
2158
- */
2159
- run(extraBody: extraBodyOptions = {}): Promise<LayoutRecord[]> {
2160
- return this.fetch(extraBody)
2161
- }
2162
-
2163
- fetch(extraBody: extraBodyOptions = {}): Promise<LayoutRecord[]> {
2164
- let trace = new Error()
2165
- return new Promise((resolve, reject) => {
2166
- // console.log(this.#toObject())
2167
- this.layout.getLayoutMeta().then(() => {
2168
- return this.layout.database.apiRequest(`${this.layout.endpoint}/records${this.generateQueryParams(extraBody)}`, {
2169
- method: "GET"
2170
- })
2171
- })
2172
- .then(async res => {
2173
- // // console.log(res)
2174
- if (res.messages[0].code === "0") {
2175
- // console.log("RESOLVING")
2176
- if (!this.layout.metadata) await this.layout.getLayoutMeta()
2177
- let data = res.response.data.map(item => {
2178
- return new LayoutRecord(this.layout, item.recordId, item.modId, item.fieldData, item.portalData)
2179
- })
2180
- resolve(data)
2181
- }
2182
- else {
2183
- reject(new FMError(res.messages[0].code, res.status, res, trace))
2184
- }
2185
- })
2186
- .catch(e => {
2187
- reject(e)
2188
- })
2189
- })
2190
- }
2191
- }
2192
-
2193
- export class Find extends RecordGetOperation {
2194
- protected queries: object[]
2195
-
2196
- constructor(layout, start = 0, limit = 100) {
2197
- super(layout)
2198
- this.setOffset(start)
2199
- this.setLimit(limit)
2200
- this.queries = []
2201
- }
2202
-
2203
- private toObject() {
2204
- let out = {query: this.queries, sort: undefined}
2205
- if (this.sort.length !== 0) out.sort = this.sort
2206
-
2207
- for (let item of Object.keys(this.scripts)) {
2208
- if (this.scripts[item]) {
2209
- out[item] = this.scripts[item].name
2210
- if (this.scripts[item].parameter) out[item + ".param"] = this.scripts[item].parameter
2211
- }
2212
- }
2213
-
2214
- if (this.limit !== 100) out["limit"] = this.limit
2215
- if (this.offset !== 0) out["offset"] = this.offset
2216
- if (this.limitPortals.length > 0) {
2217
- out["portal"] = this.limitPortals.map(p => p.portal.name)
2218
- for (let item of this.limitPortals) {
2219
- out["offset." + item.portal.name.replace(/[^0-9A-z]/g, "")] = item.offset
2220
- out["limit." + item.portal.name.replace(/[^0-9A-z]/g, "")] = item.limit
2221
- }
2222
- }
2223
-
2224
- return out
2225
- }
2226
-
2227
- addRequests(...requests) {
2228
- for (let item of requests) this.queries.push(item)
2229
- return this
2230
- }
2231
-
2232
- /**
2233
- * @deprecated in favour of .fetch()
2234
- */
2235
- run() {
2236
- return this.fetch()
2237
- }
2238
-
2239
- fetch(): Promise<LayoutRecord[]> {
2240
- let trace = new Error()
2241
- return new Promise((resolve, reject) => {
2242
- // console.log(this.#toObject())
2243
- this.layout.getLayoutMeta()
2244
- .then(() => {
2245
- return this.layout.database.apiRequest(`${this.layout.endpoint}/_find`, {
2246
- port: 443,
2247
- method: "POST",
2248
- body: JSON.stringify(this.toObject())
2249
- })
2250
- })
2251
- .then(async res => {
2252
- // // console.log(res)
2253
- if (res.messages[0].code === "0") {
2254
- // console.log("RESOLVING")
2255
- if (!this.layout.metadata) await this.layout.getLayoutMeta()
2256
- let data = res.response.data.map(item => {
2257
- return new LayoutRecord(this.layout, item.recordId, item.modId, item.fieldData, item.portalData)
2258
- })
2259
- resolve(data)
2260
- }
2261
- else {
2262
- reject(new FMError(res.messages[0].code, res.status, res, trace))
2263
- }
2264
- })
2265
- .catch(e => {
2266
- reject(e)
2267
- })
2268
- })
2269
- }
2270
- }
2271
-
2272
- export class FMError extends Error {
2273
- readonly httpStatus: number
2274
- readonly res: any
2275
- readonly code: number
2276
- readonly message: string
2277
- readonly messages: string
2278
-
2279
- constructor(code, httpStatus, res, trace?: Error) {
2280
- if (typeof code === "string") code = parseInt(code)
2281
- super(errs.find(err => err.e === code).d || "Unknown error");
2282
- this.httpStatus = httpStatus
2283
- this.res = res
2284
- this.messages = res.messages
2285
- this.code = typeof code === "string" ? parseInt(code) : code
2286
- Error.captureStackTrace(this, FMError)
2287
-
2288
- if (trace) this.stack = trace.stack
2289
- }
2290
- }
2291
-
2292
- //
2293
- // module.exports = {
2294
- // default: {FileMakerConnection}
2295
- // }
2296
-
2297
- interface AuthorizationHeaders {
2298
- "Content-Type": "application/json"
2299
- Authorization: string
2300
- }
2301
-
2302
- interface AuthorizationHeadersOAuth {
2303
- "Content-Type": "application/json"
2304
- "X-FM-Data-OAuth-RequestId": string,
2305
- "X-FM-Data-OAuth-Identifier": string
2306
- }
2307
-
2308
- function generateAuthorizationHeaders(credentials: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris): AuthorizationHeaders | AuthorizationHeadersOAuth {
2309
- switch (credentials.method) {
2310
- case "filemaker":
2311
- return {
2312
- "Content-Type": "application/json",
2313
- "Authorization": "Basic " + Buffer.from((<loginOptionsFileMaker>credentials).username + ":" + (<loginOptionsFileMaker>credentials).password).toString("base64")
2314
- } as AuthorizationHeaders
2315
- case "claris":
2316
- return {
2317
- "Content-Type": "application/json",
2318
- "Authorization": (<loginOptionsClaris>credentials).claris.fmid,
2319
- } as AuthorizationHeaders
2320
- case "oauth":
2321
- return {
2322
- "Content-Type": "application/json",
2323
- "X-FM-Data-OAuth-RequestId": (<loginOptionsOAuth>credentials).oauth.requestId,
2324
- "X-FM-Data-OAuth-Identifier": (<loginOptionsOAuth>credentials).oauth.requestIdentifier
2325
- } as AuthorizationHeadersOAuth
2326
- }
2327
- }
7
+ export default FMHost