@jd-data-limited/easy-fm 0.0.12 → 1.0.1-alpha1
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/.idea/copyright/JD_Data.xml +6 -0
- package/.idea/copyright/profiles_settings.xml +7 -0
- package/.idea/easy-fm.iml +7 -7
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/modules.xml +7 -7
- package/.idea/vcs.xml +5 -5
- package/LICENSE +21 -21
- package/README.md +211 -211
- package/decs.d.ts +6 -6
- package/docs/classes/FileMakerConnection.md +85 -85
- package/fmErrors.json +1009 -1009
- package/index.js +582 -511
- package/index.ts +715 -591
- package/package.json +34 -34
- package/tsconfig.json +8 -8
- package/.idea/php.xml +0 -18
package/index.ts
CHANGED
|
@@ -1,592 +1,716 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2022. See LICENSE file for more information
|
|
3
|
-
*/
|
|
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,
|
|
15
|
-
Request,
|
|
16
|
-
Response
|
|
17
|
-
} from 'node-fetch';
|
|
18
|
-
import { EventEmitter } from "events";
|
|
19
|
-
// @ts-ignore
|
|
20
|
-
import fs from "fs"
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
import path from "path"
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
import { fileURLToPath } from "url"
|
|
25
|
-
import * as https from "https";
|
|
26
|
-
// import * as btoa from "btoa";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
this.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2022-2023. See LICENSE file for more information
|
|
3
|
+
*/
|
|
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,
|
|
15
|
+
Request,
|
|
16
|
+
Response
|
|
17
|
+
} from 'node-fetch';
|
|
18
|
+
import { EventEmitter } from "events";
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
import fs from "fs"
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
import path from "path"
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
import { fileURLToPath } from "url"
|
|
25
|
+
import * as https from "https";
|
|
26
|
+
// import * as btoa from "btoa";
|
|
27
|
+
|
|
28
|
+
interface connectionOptions {
|
|
29
|
+
hostname: string | undefined,
|
|
30
|
+
database: databaseOptions
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface hostConnectionOptions {
|
|
34
|
+
hostname: string
|
|
35
|
+
database: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris | loginOptionsToken
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface databaseOptions {
|
|
39
|
+
method: any
|
|
40
|
+
database: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface loginOptionsToken extends databaseOptions {
|
|
44
|
+
method: "token"
|
|
45
|
+
token: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface loginOptionsOAuth extends databaseOptions {
|
|
49
|
+
method: "oauth"
|
|
50
|
+
oauth: {
|
|
51
|
+
requestId: string,
|
|
52
|
+
requestIdentifier: string
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface loginOptionsFileMaker extends databaseOptions {
|
|
57
|
+
method: "filemaker"
|
|
58
|
+
username: string,
|
|
59
|
+
password: string,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface loginOptionsClaris extends databaseOptions {
|
|
63
|
+
method: "claris"
|
|
64
|
+
claris: {
|
|
65
|
+
fmid: string
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
const errs = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'fmErrors.json')).toString())
|
|
71
|
+
|
|
72
|
+
interface Script {
|
|
73
|
+
name: string
|
|
74
|
+
parameter: string
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface recordObject {
|
|
78
|
+
recordId: number
|
|
79
|
+
modId: number,
|
|
80
|
+
fieldData: any
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class database {
|
|
84
|
+
external: boolean;
|
|
85
|
+
public name: string;
|
|
86
|
+
|
|
87
|
+
protected host: FileMakerConnection;
|
|
88
|
+
protected props: databaseOptions;
|
|
89
|
+
|
|
90
|
+
public hostname: string;
|
|
91
|
+
|
|
92
|
+
constructor(props: databaseOptions, host: FileMakerConnection = null) {
|
|
93
|
+
this.props = props
|
|
94
|
+
this.name = this.props.database
|
|
95
|
+
if (host) this.host = host
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get token() {
|
|
99
|
+
return this.host.token
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getLayout(name): layout {
|
|
103
|
+
return new layout(this, name)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async apiRequest(url: string | Request, options: any = {}): Promise<any> {
|
|
107
|
+
if (!options.headers) options.headers = {}
|
|
108
|
+
options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json"
|
|
109
|
+
options.headers["authorization"] = "Bearer " + this.host.token
|
|
110
|
+
options.rejectUnauthorized = this.host.rejectUnauthroized
|
|
111
|
+
|
|
112
|
+
let _fetch = await fetch(url, options)
|
|
113
|
+
let data = await _fetch.json()
|
|
114
|
+
return (data as any)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setGlobals(globalFields): Promise<void> {
|
|
118
|
+
// console.log({globalFields})
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
this.apiRequest(`${this.endpoint}/globals`, {
|
|
121
|
+
method: "PATCH",
|
|
122
|
+
body: JSON.stringify({globalFields})
|
|
123
|
+
}).then(res => {
|
|
124
|
+
// console.log(res)
|
|
125
|
+
if (res.messages[0].code === "0") {
|
|
126
|
+
resolve()
|
|
127
|
+
} else {
|
|
128
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
.catch(e => {
|
|
132
|
+
reject(e)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
script(name, parameter): Script {
|
|
138
|
+
return ({name, parameter} as Script)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get endpoint(): string {
|
|
142
|
+
return `https://${this.hostname || this.host.hostname}/fmi/data/v2/databases/${encodeURI(this.name)}`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get externalLoginInfo() {
|
|
146
|
+
// Returns an object that can be used when using this database object as an external source
|
|
147
|
+
let out = {
|
|
148
|
+
database: this.name,
|
|
149
|
+
username: undefined,
|
|
150
|
+
password: undefined,
|
|
151
|
+
oauthRequestId: undefined,
|
|
152
|
+
oauthIdentifier: undefined
|
|
153
|
+
}
|
|
154
|
+
switch (this.props.method) {
|
|
155
|
+
case "filemaker":
|
|
156
|
+
out.username = (<loginOptionsFileMaker>this.props).username
|
|
157
|
+
out.password = (<loginOptionsFileMaker>this.props).password
|
|
158
|
+
case "token":
|
|
159
|
+
throw "Token logins cannot be used for connecting to external sources. Please either use no login method, or open a second connection."
|
|
160
|
+
case "oauth":
|
|
161
|
+
out.oauthRequestId = (<loginOptionsOAuth>this.props).oauth.requestId
|
|
162
|
+
out.oauthIdentifier = (<loginOptionsOAuth>this.props).oauth.requestIdentifier
|
|
163
|
+
|
|
164
|
+
case "claris":
|
|
165
|
+
throw "Claris logins cannot be used for connecting to external sources. Please either use no login method, or open a second connection."
|
|
166
|
+
}
|
|
167
|
+
return out
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default class FileMakerConnection extends database{
|
|
172
|
+
private _token: any;
|
|
173
|
+
private username: any;
|
|
174
|
+
private password: any;
|
|
175
|
+
public readonly rejectUnauthroized: boolean;
|
|
176
|
+
externalSources: database[];
|
|
177
|
+
|
|
178
|
+
constructor(conn: hostConnectionOptions, externalDataSources:databaseOptions[] = [], rejectUnauthorized = true) {
|
|
179
|
+
super(conn.database);
|
|
180
|
+
this.host = this
|
|
181
|
+
this.hostname = conn.hostname
|
|
182
|
+
this.externalSources = externalDataSources.map(i => {let e = new database(i, this); e.external = true; return e;})
|
|
183
|
+
this.rejectUnauthroized = rejectUnauthorized
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
logout(): Promise<void> {
|
|
187
|
+
return new Promise(async (resolve, reject) => {
|
|
188
|
+
if (!this.token) reject(new Error("Not logged in"))
|
|
189
|
+
|
|
190
|
+
let _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
|
|
191
|
+
method: "DELETE",
|
|
192
|
+
headers: {
|
|
193
|
+
"content-type": "application/json"
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
let data = await _fetch.json()
|
|
197
|
+
// console.log(data)
|
|
198
|
+
this._token = null
|
|
199
|
+
this.name = null
|
|
200
|
+
this.hostname = null
|
|
201
|
+
resolve()
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
login() {
|
|
206
|
+
return new Promise<string>((resolve, reject) => {
|
|
207
|
+
if (this.token) throw new Error("Already logged in. Run logout() first")
|
|
208
|
+
|
|
209
|
+
if (this.props.method === "filemaker") {
|
|
210
|
+
this.username = (<loginOptionsFileMaker>this.props).username
|
|
211
|
+
this.name = this.props.database
|
|
212
|
+
|
|
213
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
214
|
+
hostname: this.hostname,
|
|
215
|
+
port: 443,
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: {
|
|
218
|
+
"Content-Type": "application/json",
|
|
219
|
+
"Authorization": "Basic " + Buffer.from(this.username + ":" + (<loginOptionsFileMaker>this.props).password).toString("base64")
|
|
220
|
+
}
|
|
221
|
+
}).then(async res => {
|
|
222
|
+
if (res.status === 200) {
|
|
223
|
+
this._token = res.headers.get('x-fm-data-access-token')
|
|
224
|
+
resolve(this._token)
|
|
225
|
+
} else {
|
|
226
|
+
let _res = <any>(await res.json())
|
|
227
|
+
reject(new FMError(_res.messages[0].code, _res.status, res))
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
.catch(e => {
|
|
231
|
+
reject(e)
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
else if (this.props.method === "token") {
|
|
235
|
+
this._token = (<loginOptionsToken>this.props).token
|
|
236
|
+
this.name = this.props.database
|
|
237
|
+
resolve(this.token)
|
|
238
|
+
}
|
|
239
|
+
else if (this.props.method === "oauth") {
|
|
240
|
+
this.name = this.props.database
|
|
241
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: {
|
|
244
|
+
"Content-Type": "application/json",
|
|
245
|
+
"X-FM-Data-OAuth-RequestId": (<loginOptionsOAuth>this.props).oauth.requestId,
|
|
246
|
+
"X-FM-Data-OAuth-Identifier": (<loginOptionsOAuth>this.props).oauth.requestIdentifier
|
|
247
|
+
},
|
|
248
|
+
body: "{}"
|
|
249
|
+
})
|
|
250
|
+
.then(res => res.json())
|
|
251
|
+
.then(res => {
|
|
252
|
+
let _res = <any>res
|
|
253
|
+
this._token = _res.headers["x-fm-data-access-token"]
|
|
254
|
+
resolve(this.token)
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
else if (this.props.method === "claris") {
|
|
258
|
+
this.name = this.props.database
|
|
259
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: {
|
|
262
|
+
"Content-Type": "application/json",
|
|
263
|
+
"Authorization": (<loginOptionsClaris>this.props).claris.fmid,
|
|
264
|
+
},
|
|
265
|
+
body: "{}"
|
|
266
|
+
})
|
|
267
|
+
.then(res => res.json())
|
|
268
|
+
.then(res => {
|
|
269
|
+
let _res = <any>res
|
|
270
|
+
this._token = _res.headers["x-fm-data-access-token"]
|
|
271
|
+
resolve(this.token)
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
get token() {
|
|
278
|
+
return this._token
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
class layout {
|
|
283
|
+
readonly database: database;
|
|
284
|
+
protected name: string;
|
|
285
|
+
metadata: any;
|
|
286
|
+
constructor(database: database, name: string) {
|
|
287
|
+
this.database = database
|
|
288
|
+
this.name = name
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
get endpoint() {
|
|
292
|
+
return `${this.database.endpoint}/layouts/${this.name}`
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
getScript(script) {
|
|
296
|
+
return new script(this, script)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
createRecord(): Promise<record | Error | void> {
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
// Get the layout's metadata
|
|
302
|
+
this.getLayoutMeta().then(layout => {
|
|
303
|
+
let fields = {}
|
|
304
|
+
for (let _field of this.metadata.fieldMetaData) {
|
|
305
|
+
fields[_field.name] = ""
|
|
306
|
+
}
|
|
307
|
+
resolve(new record(this, -1, 0, fields))
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
getRecord(recordId): record {
|
|
313
|
+
return new record(this, recordId)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
newFind(): find {
|
|
317
|
+
return new find(this)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
runScript(script): Promise<string | FMError | Error> {
|
|
321
|
+
return new Promise(async (resolve, reject) => {
|
|
322
|
+
let url = `${this.endpoint}/script/${encodeURI(script.name)}`
|
|
323
|
+
if (script.parameter) url += "?" + encodeURI(script.parameter)
|
|
324
|
+
this.database.apiRequest(url, {
|
|
325
|
+
port: 443,
|
|
326
|
+
method: "GET"
|
|
327
|
+
})
|
|
328
|
+
.then(res => {
|
|
329
|
+
if (res.messages[0].code === "0") {
|
|
330
|
+
resolve(res.response)
|
|
331
|
+
} else {
|
|
332
|
+
// console.log(res)
|
|
333
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
.catch(e => {
|
|
337
|
+
reject(e)
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
public getLayoutMeta(): Promise<layout | FMError> {
|
|
343
|
+
return new Promise((resolve, reject) => {
|
|
344
|
+
this.database.apiRequest(this.endpoint).then(res => {
|
|
345
|
+
this.metadata = res.response
|
|
346
|
+
resolve(this)
|
|
347
|
+
})
|
|
348
|
+
.catch(e => reject(e))
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
class record extends EventEmitter {
|
|
354
|
+
readonly layout: layout;
|
|
355
|
+
public recordId: number;
|
|
356
|
+
modId: number;
|
|
357
|
+
fields: field[];
|
|
358
|
+
readonly portals: portal[];
|
|
359
|
+
private portalData: any[];
|
|
360
|
+
|
|
361
|
+
constructor(layout, recordId, modId = recordId, fieldData = {}, portalData = null) {
|
|
362
|
+
super();
|
|
363
|
+
this.layout = layout
|
|
364
|
+
this.recordId = recordId
|
|
365
|
+
this.modId = modId
|
|
366
|
+
this.fields = this.processFieldData(fieldData)
|
|
367
|
+
this.portals = []
|
|
368
|
+
if (portalData) {
|
|
369
|
+
this.processPortalData(portalData)
|
|
370
|
+
}
|
|
371
|
+
this.on("saved", () => {
|
|
372
|
+
for (let field of this.fields) field.edited = false
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
get endpoint(): string {
|
|
377
|
+
return `${this.layout.endpoint}/records/${this.recordId}`
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
get edited(): boolean {
|
|
381
|
+
return !!this.fields.find(i => i.edited)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private processPortalData(portalData): void {
|
|
385
|
+
for (let item of Object.keys(portalData)) {
|
|
386
|
+
this.portals.push(new portal(this, item, portalData[item].map(item => {
|
|
387
|
+
let fieldData = item
|
|
388
|
+
delete fieldData.recordId
|
|
389
|
+
delete fieldData.modId
|
|
390
|
+
return new portalItem(this, item.recordId, item.modId, fieldData)
|
|
391
|
+
})))
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
processFieldData(fieldData): field[] {
|
|
396
|
+
return Object.keys(fieldData).map(item => {
|
|
397
|
+
return new field(this, item, fieldData[item])
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
get() {
|
|
402
|
+
if (this.recordId === -1) {throw "Cannot get this record until a commit() is done."}
|
|
403
|
+
return new Promise(async (resolve, reject) => {
|
|
404
|
+
if (!this.layout.metadata) await this.layout.getLayoutMeta()
|
|
405
|
+
|
|
406
|
+
this.layout.database.apiRequest(this.endpoint, {
|
|
407
|
+
port: 443,
|
|
408
|
+
method: "GET"
|
|
409
|
+
})
|
|
410
|
+
.then(res => {
|
|
411
|
+
if (res.messages[0].code === "0") {
|
|
412
|
+
// console.log(res, res.response.data)
|
|
413
|
+
this.modId = res.response.data[0].modId
|
|
414
|
+
this.fields = this.processFieldData(res.response.data[0].fieldData)
|
|
415
|
+
this.portalData = []
|
|
416
|
+
if (res.response.data[0].portalData) this.processPortalData(res.response.data[0].portalData)
|
|
417
|
+
resolve(this)
|
|
418
|
+
} else {
|
|
419
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
420
|
+
}
|
|
421
|
+
})
|
|
422
|
+
.catch(e => {
|
|
423
|
+
reject(e)
|
|
424
|
+
})
|
|
425
|
+
})
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
commit(extraBody = {
|
|
429
|
+
fieldData: undefined
|
|
430
|
+
}): Promise<record | FMError> {
|
|
431
|
+
return new Promise(async (resolve, reject) => {
|
|
432
|
+
let data = this.toObject()
|
|
433
|
+
delete data.recordId
|
|
434
|
+
delete data.modId
|
|
435
|
+
|
|
436
|
+
if (this.recordId === -1) {
|
|
437
|
+
// This is a new record
|
|
438
|
+
extraBody.fieldData = data.fieldData
|
|
439
|
+
|
|
440
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/records`, {
|
|
441
|
+
port: 443,
|
|
442
|
+
method: "POST",
|
|
443
|
+
body: JSON.stringify(extraBody)
|
|
444
|
+
})
|
|
445
|
+
.then(res => {
|
|
446
|
+
if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
|
|
447
|
+
reject(new FMError(res.response.scriptError, res.status, res))
|
|
448
|
+
}
|
|
449
|
+
else if (res.messages[0].code === "0") {
|
|
450
|
+
console.log(res)
|
|
451
|
+
this.recordId = parseInt(res.response.recordId)
|
|
452
|
+
this.modId = parseInt(res.response.modId)
|
|
453
|
+
resolve(this)
|
|
454
|
+
} else {
|
|
455
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
sh .catch(e => {
|
|
459
|
+
reject(e)
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
return
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
for (let item of Object.keys(extraBody)) data[item] = extraBody[item]
|
|
466
|
+
this.layout.database.apiRequest(this.endpoint, {
|
|
467
|
+
port: 443,
|
|
468
|
+
method: "PATCH",
|
|
469
|
+
body: JSON.stringify(data)
|
|
470
|
+
})
|
|
471
|
+
.then(res => {
|
|
472
|
+
if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
|
|
473
|
+
reject(new FMError(res.response.scriptError, res.status, res))
|
|
474
|
+
}
|
|
475
|
+
else if (res.messages[0].code === "0") {
|
|
476
|
+
console.log(res)
|
|
477
|
+
this.modId = res.response.modId
|
|
478
|
+
this.emit("saved")
|
|
479
|
+
resolve(this)
|
|
480
|
+
} else {
|
|
481
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
482
|
+
}
|
|
483
|
+
})
|
|
484
|
+
.catch(e => {
|
|
485
|
+
reject(e)
|
|
486
|
+
})
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
getField(field) {
|
|
491
|
+
return this.fields.find(_field => _field.id === field)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
getPortal(portal) {
|
|
495
|
+
return this.portals.find(p => p.name === portal)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
toObject(filter = (a) => a.edited,
|
|
499
|
+
portalFilter = (a) => a.records.find(record => record.edited),
|
|
500
|
+
portalRowFilter = (a) => a.edited,
|
|
501
|
+
portalFieldFilter = (a) => a.edited
|
|
502
|
+
): recordObject {
|
|
503
|
+
let fields_processed = {}
|
|
504
|
+
for (let field of this.fields.filter(field => filter(field))) {
|
|
505
|
+
fields_processed[field.id] = field.value
|
|
506
|
+
}
|
|
507
|
+
let obj = {
|
|
508
|
+
"recordId": this.recordId,
|
|
509
|
+
"modId": this.modId,
|
|
510
|
+
"fieldData": fields_processed
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Check if there's been any edited portal information
|
|
514
|
+
let portals = this.portals.filter(a => portalFilter(a))
|
|
515
|
+
if (portals) {
|
|
516
|
+
obj["portalData"] = {}
|
|
517
|
+
for (let portal of portals) {
|
|
518
|
+
obj["portalData"][portal.name] = portal.records.filter(a => portalRowFilter(a)).map(record => {
|
|
519
|
+
return record.toObject(portalFieldFilter)
|
|
520
|
+
})
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return obj
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
class field {
|
|
528
|
+
record: record;
|
|
529
|
+
id: number;
|
|
530
|
+
value: string | number;
|
|
531
|
+
edited: boolean;
|
|
532
|
+
constructor(record, id, contents) {
|
|
533
|
+
this.record = record
|
|
534
|
+
this.id = id
|
|
535
|
+
this.value = contents
|
|
536
|
+
this.edited = false
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
set(content) {
|
|
540
|
+
if (this.dataType === "container") throw "Cannot set container value using set(). Use upload() instead."
|
|
541
|
+
this.value = content
|
|
542
|
+
this.edited = true
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
get dataType() {
|
|
546
|
+
if (!this.record.layout.metadata) return "unknown"
|
|
547
|
+
if (this.record instanceof portalItem) {
|
|
548
|
+
return this.record.layout.metadata.portalMetaData.find(i => i.name === this.id).result || "unknown"
|
|
549
|
+
} else {
|
|
550
|
+
return this.record.layout.metadata.fieldMetaData.find(i => i.name === this.id).result || "unknown"
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
upload(buffer: Buffer, filename: string, mime: string): Promise<void | FMError> {
|
|
555
|
+
if (this.dataType !== "container") throw "Cannot upload a file to the field; " + this.id + " (not a container field)"
|
|
556
|
+
return new Promise(async (resolve, reject) => {
|
|
557
|
+
let form = new FormData()
|
|
558
|
+
form.append("upload", new File([buffer], filename, {type: mime}))
|
|
559
|
+
|
|
560
|
+
let _fetch = await fetch(`${this.record.endpoint}/containers/${this.id}/1`, {
|
|
561
|
+
method: "POST",
|
|
562
|
+
headers: {"Authorization": "Bearer " + this.record.layout.database.token},
|
|
563
|
+
body: form
|
|
564
|
+
}).then(res => res.json())
|
|
565
|
+
.then(data => {
|
|
566
|
+
let _res = data as any
|
|
567
|
+
if (_res.messages[0].code === "0") {
|
|
568
|
+
resolve()
|
|
569
|
+
} else {
|
|
570
|
+
reject(new FMError(_res.messages[0].code, _res.status, data))
|
|
571
|
+
}
|
|
572
|
+
})
|
|
573
|
+
.catch(e => {reject(e)})
|
|
574
|
+
})
|
|
575
|
+
}
|
|
576
|
+
download() {
|
|
577
|
+
return new Promise((resolve, reject) => {
|
|
578
|
+
https.get(this.value.toString(), (res) => {
|
|
579
|
+
resolve(res)
|
|
580
|
+
})
|
|
581
|
+
})
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
class portal {
|
|
586
|
+
readonly record: record;
|
|
587
|
+
readonly name: string;
|
|
588
|
+
public records: portalItem[];
|
|
589
|
+
|
|
590
|
+
constructor(record: record, name: string, records: portalItem[] = []) {
|
|
591
|
+
this.record = record
|
|
592
|
+
this.name = name
|
|
593
|
+
this.records = records
|
|
594
|
+
for (let item of this.records) item.attachPortal(this)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
class portalItem extends record {
|
|
599
|
+
protected portal: portal;
|
|
600
|
+
|
|
601
|
+
constructor(record, recordId, modId = recordId, fieldData = {}) {
|
|
602
|
+
super(record.layout, recordId, modId, fieldData);
|
|
603
|
+
record.on("saved", () => {
|
|
604
|
+
this.emit("saved")
|
|
605
|
+
})
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
attachPortal(portal) {
|
|
609
|
+
this.portal = portal
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
commit(extraBody = {fieldData: undefined}) {
|
|
613
|
+
return this.portal.record.commit(extraBody)
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
toObject(fieldFilter): any {
|
|
617
|
+
super.toObject()
|
|
618
|
+
let res = {
|
|
619
|
+
recordId: this.recordId, modId: this.modId
|
|
620
|
+
} as recordObject
|
|
621
|
+
for (let field of this.fields.filter(a => fieldFilter(a))) res[field.id] = field.value
|
|
622
|
+
// console.log(res)
|
|
623
|
+
return res
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
class find {
|
|
628
|
+
protected layout: layout
|
|
629
|
+
protected queries: object[]
|
|
630
|
+
protected scripts: object
|
|
631
|
+
protected sort: object[]
|
|
632
|
+
|
|
633
|
+
constructor(layout) {
|
|
634
|
+
this.layout = layout
|
|
635
|
+
this.queries = []
|
|
636
|
+
this.scripts = {
|
|
637
|
+
"script": null, // Runs after everything
|
|
638
|
+
"script.prerequeset": null, // Runs before the request
|
|
639
|
+
"script.presort": null // Runs before the sort
|
|
640
|
+
}
|
|
641
|
+
this.sort = []
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private toObject() {
|
|
645
|
+
let out = {query: this.queries, sort: undefined}
|
|
646
|
+
if (this.sort.length !== 0) out.sort = this.sort
|
|
647
|
+
|
|
648
|
+
for (let item of Object.keys(this.scripts)) {
|
|
649
|
+
if (this.scripts[item]) {
|
|
650
|
+
out[item] = this.scripts[item].name
|
|
651
|
+
if (this.scripts[item].parameter) out[item + ".param"] = this.scripts[item].parameter
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return out
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
addRequests(...requests) {
|
|
659
|
+
for (let item of requests) this.queries.push(item)
|
|
660
|
+
return this
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
addSort(fieldName, sortOrder) {
|
|
664
|
+
this.sort.push({fieldName, sortOrder})
|
|
665
|
+
return this
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
find() {
|
|
669
|
+
return new Promise((resolve, reject) => {
|
|
670
|
+
// console.log(this.#toObject())
|
|
671
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/_find`, {
|
|
672
|
+
port: 443,
|
|
673
|
+
method: "POST",
|
|
674
|
+
body: JSON.stringify(this.toObject())
|
|
675
|
+
}).then(async res => {
|
|
676
|
+
// // console.log(res)
|
|
677
|
+
if (res.messages[0].code === "0") {
|
|
678
|
+
// console.log("RESOLVING")
|
|
679
|
+
if (!this.layout.metadata) await this.layout.getLayoutMeta()
|
|
680
|
+
let data = res.response.data.map(item => {
|
|
681
|
+
return new record(this.layout, item.recordId, item.modId, item.fieldData, item.portalData)
|
|
682
|
+
})
|
|
683
|
+
resolve(data)
|
|
684
|
+
} else {
|
|
685
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
686
|
+
}
|
|
687
|
+
})
|
|
688
|
+
.catch(e => {
|
|
689
|
+
reject(e)
|
|
690
|
+
})
|
|
691
|
+
})
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export class FMError extends Error {
|
|
696
|
+
readonly httpStatus: number
|
|
697
|
+
readonly res: any
|
|
698
|
+
readonly code: number
|
|
699
|
+
readonly message: string
|
|
700
|
+
readonly messages: string
|
|
701
|
+
|
|
702
|
+
constructor(code, httpStatus, res) {
|
|
703
|
+
if (typeof code === "string") code = parseInt(code)
|
|
704
|
+
super(errs.find(err => err.e === code).d || "Unknown error");
|
|
705
|
+
this.httpStatus = httpStatus
|
|
706
|
+
this.res = res
|
|
707
|
+
this.messages = res.messages
|
|
708
|
+
this.code = typeof code === "string" ? parseInt(code) : code
|
|
709
|
+
Error.captureStackTrace(this, FMError)
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
//
|
|
714
|
+
// module.exports = {
|
|
715
|
+
// default: {FileMakerConnection}
|
|
592
716
|
// }
|