@jd-data-limited/easy-fm 4.0.14 → 4.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 (65) hide show
  1. package/package.json +12 -6
  2. package/src/FMError.d.ts +2 -2
  3. package/src/FMError.js +507 -508
  4. package/src/FMError.ts +510 -511
  5. package/src/bin/cli.js +7 -5
  6. package/src/bin/generateTypes.js +215 -121
  7. package/src/connection/FMHost.d.ts +28 -6
  8. package/src/connection/FMHost.js +52 -25
  9. package/src/connection/FMHost.ts +75 -43
  10. package/src/connection/HostBase.d.ts +7 -3
  11. package/src/connection/HostBase.ts +8 -4
  12. package/src/connection/database.d.ts +50 -17
  13. package/src/connection/database.js +94 -66
  14. package/src/connection/database.ts +125 -105
  15. package/src/connection/databaseBase.d.ts +11 -6
  16. package/src/connection/databaseBase.ts +9 -8
  17. package/src/connection/generateAuthorizationHeaders.d.ts +1 -1
  18. package/src/connection/generateAuthorizationHeaders.js +10 -10
  19. package/src/connection/generateAuthorizationHeaders.ts +21 -21
  20. package/src/databaseStructure.d.ts +2 -4
  21. package/src/databaseStructure.ts +3 -5
  22. package/src/index.d.ts +17 -17
  23. package/src/index.js +14 -14
  24. package/src/index.ts +28 -25
  25. package/src/layouts/layout.d.ts +17 -6
  26. package/src/layouts/layout.js +19 -8
  27. package/src/layouts/layout.ts +33 -23
  28. package/src/layouts/layoutBase.d.ts +5 -5
  29. package/src/layouts/layoutBase.ts +6 -6
  30. package/src/layouts/layoutInterface.d.ts +3 -5
  31. package/src/layouts/layoutInterface.ts +4 -6
  32. package/src/layouts/layoutRecordManager.d.ts +29 -6
  33. package/src/layouts/layoutRecordManager.js +32 -9
  34. package/src/layouts/layoutRecordManager.ts +42 -19
  35. package/src/layouts/recordFieldsMap.d.ts +2 -4
  36. package/src/layouts/recordFieldsMap.ts +2 -4
  37. package/src/models/apiResults.d.ts +7 -13
  38. package/src/models/apiResults.ts +58 -58
  39. package/src/records/field.d.ts +42 -12
  40. package/src/records/field.js +69 -59
  41. package/src/records/field.ts +102 -81
  42. package/src/records/getOperations/recordGetOperation.d.ts +44 -24
  43. package/src/records/getOperations/recordGetOperation.js +89 -58
  44. package/src/records/getOperations/recordGetOperation.ts +137 -108
  45. package/src/records/layoutRecord.d.ts +28 -14
  46. package/src/records/layoutRecord.js +75 -65
  47. package/src/records/layoutRecord.ts +126 -123
  48. package/src/records/layoutRecordBase.d.ts +2 -2
  49. package/src/records/layoutRecordBase.ts +3 -3
  50. package/src/records/portal.d.ts +19 -5
  51. package/src/records/portal.js +19 -5
  52. package/src/records/portal.ts +28 -16
  53. package/src/records/portalBase.d.ts +5 -5
  54. package/src/records/portalBase.ts +6 -6
  55. package/src/records/portalRecord.d.ts +16 -8
  56. package/src/records/portalRecord.js +16 -6
  57. package/src/records/portalRecord.ts +29 -19
  58. package/src/records/recordBase.d.ts +17 -7
  59. package/src/records/recordBase.js +32 -22
  60. package/src/records/recordBase.ts +50 -43
  61. package/src/types.d.ts +21 -24
  62. package/src/types.ts +55 -59
  63. package/src/utils/query.d.ts +12 -5
  64. package/src/utils/query.js +28 -9
  65. package/src/utils/query.ts +40 -15
@@ -2,88 +2,120 @@
2
2
  * Copyright (c) 2023-2024. See LICENSE file for more information
3
3
  */
4
4
 
5
- import {generateAuthorizationHeaders} from "./generateAuthorizationHeaders.js";
6
- import {FMError} from "../FMError.js";
7
- import {Database} from "./database.js";
8
- import {HostBase} from "./HostBase.js"
5
+ import {generateAuthorizationHeaders} from './generateAuthorizationHeaders.js'
6
+ import {FMError} from '../FMError.js'
7
+ import {Database} from './database.js'
8
+ import {type HostBase} from './HostBase.js'
9
9
  import {
10
- databaseOptionsWithExternalSources,
11
- FMHostMetadata,
12
- loginOptionsClaris,
13
- loginOptionsFileMaker,
14
- loginOptionsOAuth
15
- } from "../types.js";
16
- import {ApiResults} from "../models/apiResults.js";
17
- import {DatabaseStructure} from "../databaseStructure.js";
10
+ type databaseOptionsWithExternalSources,
11
+ type FMHostMetadata,
12
+ type loginOptionsClaris,
13
+ type loginOptionsFileMaker,
14
+ type loginOptionsOAuth
15
+ } from '../types.js'
16
+ import {type ApiResults} from '../models/apiResults.js'
17
+ import {type DatabaseStructure} from '../databaseStructure.js'
18
+ import {type Moment} from 'moment'
18
19
 
20
+ /**
21
+ * Represents a FileMaker host.
22
+ * @implements {HostBase}
23
+ */
19
24
  export default class FMHost implements HostBase {
20
25
  readonly hostname: string
21
- readonly timezoneOffset: number
26
+ readonly timezoneOffsetFunc: (moment: Moment) => number
22
27
  readonly verify: boolean
23
28
  _metadata: FMHostMetadata | null = null
24
29
 
25
- constructor(_hostname: string, timezoneOffset = 0 - (new Date()).getTimezoneOffset(), verify = true) {
26
- if (!(/^https?:\/\//).test(_hostname)) throw "hostname MUST begin with either http:// or https://"
30
+ constructor (
31
+ _hostname: string,
32
+ timezoneOffset = (moment: Moment) => 0 - (new Date()).getTimezoneOffset(),
33
+ verify = true
34
+ ) {
35
+ if (!(/^https?:\/\//).test(_hostname)) throw new Error('hostname MUST begin with either http:// or https://')
27
36
  this.hostname = _hostname
28
- this.timezoneOffset = timezoneOffset
37
+ this.timezoneOffsetFunc = timezoneOffset
29
38
  this.verify = verify
30
39
  }
31
40
 
32
- get metadata(): FMHostMetadata {
33
- return this._metadata || {
41
+ get metadata (): FMHostMetadata {
42
+ return this._metadata ?? {
34
43
  productInfo: {
35
44
  buildDate: new Date(),
36
- name: "",
37
- version: "",
38
- dateFormat: "",
39
- timeFormat: "",
40
- timeStampFormat: ""
45
+ name: '',
46
+ version: '',
47
+ dateFormat: 'MM/dd/yyyy',
48
+ timeFormat: 'HH:mm:ss',
49
+ timeStampFormat: 'MM/dd/yyyy HH:mm:ss'
41
50
  }
42
- };
51
+ }
52
+ }
53
+
54
+ get dateFormat () {
55
+ return this.metadata.productInfo.dateFormat
56
+ .replace('dd', 'DD')
57
+ .replace('yyyy', 'YYYY')
43
58
  }
44
59
 
45
- async listDatabases(credentials?: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris) {
60
+ get timeFormat () { return this.metadata.productInfo.timeFormat }
61
+ get timeStampFormat () {
62
+ return this.metadata.productInfo.timeStampFormat
63
+ .replace('dd', 'DD')
64
+ .replace('yyyy', 'YYYY')
65
+ }
66
+
67
+ /**
68
+ * Retrieves a list of databases from the FileMaker Server.
69
+ *
70
+ * @param {loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris} [credentials] - Optional credentials required for authentication.
71
+ * @throws {FMError} If the request to the FileMaker Server fails or if the response contains an error.
72
+ * @returns {Promise<any[]>} A promise that resolves to an array of database objects if successful.
73
+ */
74
+ async listDatabases (credentials?: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris) {
46
75
  let headers = {}
47
76
  if (credentials) {
48
77
  headers = generateAuthorizationHeaders(credentials)
49
78
  }
50
79
 
51
- let _fetch = await fetch(`${this.hostname}/fmi/data/v2/databases`, {
52
- method: "GET",
80
+ const _fetch = await fetch(`${this.hostname}/fmi/data/v2/databases`, {
81
+ method: 'GET',
53
82
  headers
54
83
  })
55
- let data = await _fetch.json() as ApiResults<{databases: any}>
84
+ const data = await _fetch.json() as ApiResults<{ databases: any }>
56
85
  // console.log(data.messages[0])
57
86
 
58
- if (data.messages[0].code === "0" && data.response) {
87
+ if (data.messages[0].code === '0' && data.response) {
59
88
  return data.response.databases
60
- }
61
- else {
62
- // @ts-ignore
63
- throw new FMError(data.messages[0].code, data.status, data)
89
+ } else {
90
+ throw new FMError(data.messages[0].code, _fetch.status, data)
64
91
  }
65
92
  }
66
93
 
94
+ /**
95
+ * Creates a new database connection with the specified options.
96
+ *
97
+ * @template T - The type of the database structure.
98
+ * @param {databaseOptionsWithExternalSources} data - The options for the database, including external sources.
99
+ * @return {Database<T>} A new Database instance.
100
+ */
67
101
  database<T extends DatabaseStructure>(data: databaseOptionsWithExternalSources) {
68
102
  return new Database<T>(this, data)
69
103
  }
70
104
 
71
- async getMetadata() {
105
+ async getMetadata () {
72
106
  if (this._metadata) return this._metadata
73
107
 
74
- let _fetch = await fetch(`${this.hostname}/fmi/data/v2/productInfo`, {
75
- method: "GET",
108
+ const _fetch = await fetch(`${this.hostname}/fmi/data/v2/productInfo`, {
109
+ method: 'GET'
76
110
  })
77
- let data = await _fetch.json() as ApiResults<FMHostMetadata>
111
+ const data = await _fetch.json() as ApiResults<FMHostMetadata>
78
112
  // console.log(data.messages[0])
79
113
 
80
- if (data.messages[0].code === "0" && data.response) {
114
+ if (data.messages[0].code === '0' && data.response) {
81
115
  this._metadata = data.response
82
116
  return data.response
83
- }
84
- else {
85
- // @ts-ignore
86
- throw new FMError(data.messages[0].code, data.status, data)
117
+ } else {
118
+ throw new FMError(data.messages[0].code, _fetch.status, data)
87
119
  }
88
120
  }
89
- }
121
+ }
@@ -1,8 +1,12 @@
1
- import { FMHostMetadata } from "../types.js";
1
+ import { type FMHostMetadata } from '../types.js';
2
+ import { type Moment } from 'moment';
2
3
  export interface HostBase {
3
4
  readonly hostname: string;
4
- readonly timezoneOffset: number;
5
+ readonly timezoneOffsetFunc: (moment: Moment) => number;
5
6
  readonly verify: boolean;
6
7
  metadata: FMHostMetadata;
7
- getMetadata(): PromiseLike<any>;
8
+ getMetadata: () => PromiseLike<any>;
9
+ timeFormat: string;
10
+ dateFormat: string;
11
+ timeStampFormat: string;
8
12
  }
@@ -2,12 +2,16 @@
2
2
  * Copyright (c) 2023-2024. See LICENSE file for more information
3
3
  */
4
4
 
5
- import {FMHostMetadata} from "../types.js";
5
+ import {type FMHostMetadata} from '../types.js'
6
+ import {type Moment} from 'moment'
6
7
 
7
8
  export interface HostBase {
8
9
  readonly hostname: string
9
- readonly timezoneOffset: number
10
+ readonly timezoneOffsetFunc: (moment: Moment) => number
10
11
  readonly verify: boolean
11
12
  metadata: FMHostMetadata
12
- getMetadata(): PromiseLike<any>
13
- }
13
+ getMetadata: () => PromiseLike<any>
14
+ timeFormat: string
15
+ dateFormat: string
16
+ timeStampFormat: string
17
+ }
@@ -1,33 +1,66 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
- import { EventEmitter } from "events";
4
- import { LayoutInterface } from "../layouts/layoutInterface.js";
5
- import { Layout } from "../layouts/layout.js";
6
- import { databaseOptionsWithExternalSources, Script } from "../types.js";
7
- import { HostBase } from "./HostBase.js";
8
- import { DatabaseBase } from "./databaseBase.js";
9
- import { ApiResults } from "../models/apiResults.js";
10
- import { DatabaseStructure } from "../databaseStructure.js";
11
- import { IncomingMessage } from "http";
3
+ import { EventEmitter } from 'events';
4
+ import { type LayoutInterface } from '../layouts/layoutInterface.js';
5
+ import { Layout } from '../layouts/layout.js';
6
+ import { type databaseOptionsWithExternalSources, type Script } from '../types.js';
7
+ import { type HostBase } from './HostBase.js';
8
+ import { type DatabaseBase } from './databaseBase.js';
9
+ import { type ApiResults } from '../models/apiResults.js';
10
+ import { type DatabaseStructure } from '../databaseStructure.js';
11
+ import { type IncomingMessage } from 'http';
12
+ /**
13
+ * Represents a database connection.
14
+ * @template T - The structure of the database.
15
+ */
12
16
  export declare class Database<T extends DatabaseStructure> extends EventEmitter implements DatabaseBase {
13
17
  private _token;
14
18
  readonly host: HostBase;
15
- private connection_details;
19
+ private readonly connection_details;
16
20
  private cookies;
17
21
  readonly name: string;
18
22
  readonly debug: boolean;
19
23
  constructor(host: HostBase, conn: databaseOptionsWithExternalSources);
20
24
  private generateExternalSourceLogin;
25
+ /**
26
+ * Logs out the user by deleting the current session token.
27
+ * Throws an error if the user is not logged in.
28
+ *
29
+ * @returns {Promise<void>} A promise that resolves with no value once the logout is successful.
30
+ * @throws {Error} Throws an error if the user is not logged in.
31
+ */
21
32
  logout(): Promise<void>;
22
- login(forceLogin?: boolean): Promise<any>;
23
- get token(): any;
33
+ /**
34
+ * Logs in to the database. Not required, as this is often done automatically
35
+ *
36
+ * @param {boolean} [forceLogin=false] - Whether to force login even if already logged in.
37
+ * @throws {Error} - Throws an error if already logged in and forceLogin is false.
38
+ * @throws {FMError} - Throws an FMError if login fails.
39
+ * @return {Promise<string>} - Returns a promise that resolves to the access token upon successful login.
40
+ */
41
+ login(forceLogin?: boolean): Promise<string>;
42
+ get token(): string;
43
+ /**
44
+ * Returns the endpoint URL for the database connection.
45
+ *
46
+ * @returns {string} The endpoint URL.
47
+ */
24
48
  get endpoint(): string;
25
- apiRequestRaw(url: string | Request, options?: any, autoRelogin?: boolean): Promise<Response>;
26
- apiRequestJSON<T = any>(url: string | Request, options?: any): Promise<ApiResults<T>>;
49
+ _apiRequestRaw(url: string | Request, options?: RequestInit & {
50
+ headers?: Record<string, string>;
51
+ }, autoRelogin?: boolean): Promise<Response>;
52
+ _apiRequestJSON<T = any>(url: string | Request, options?: RequestInit & {
53
+ headers?: Record<string, string>;
54
+ }): Promise<ApiResults<T>>;
55
+ /**
56
+ * Retrieves a list of layouts in the current FileMaker database.
57
+ *
58
+ * @returns {Promise<Layout[]>} A promise that resolves to an array of Layout objects.
59
+ * @throws {FMError} If there was an error retrieving the layouts.
60
+ */
27
61
  listLayouts(): Promise<Layout<LayoutInterface>[]>;
28
- getLayout<R extends keyof T["layouts"]>(name: R): Layout<T["layouts"][R]>;
29
- getLayout<R extends LayoutInterface>(name: string): Layout<R>;
62
+ layout<R extends keyof T['layouts']>(name: R): Layout<T['layouts'][R]>;
63
+ layout<R extends LayoutInterface>(name: string): Layout<R>;
30
64
  script(name: string, parameter?: string): Script;
31
- _tokenExpired(): void;
32
65
  streamURL(url: string): Promise<IncomingMessage>;
33
66
  }
@@ -1,13 +1,17 @@
1
1
  /*
2
2
  * Copyright (c) 2023-2024. See LICENSE file for more information
3
3
  */
4
- import { EventEmitter } from "events";
5
- import { generateAuthorizationHeaders } from "./generateAuthorizationHeaders.js";
6
- import { FMError } from "../FMError.js";
7
- import { Layout } from "../layouts/layout.js";
8
- import HTTP_REDIRECT from "follow-redirects";
4
+ import { EventEmitter } from 'events';
5
+ import { generateAuthorizationHeaders } from './generateAuthorizationHeaders.js';
6
+ import { FMError } from '../FMError.js';
7
+ import { Layout } from '../layouts/layout.js';
8
+ import HTTP_REDIRECT from 'follow-redirects';
9
+ /**
10
+ * Represents a database connection.
11
+ * @template T - The structure of the database.
12
+ */
9
13
  export class Database extends EventEmitter {
10
- _token;
14
+ _token = '';
11
15
  host;
12
16
  connection_details;
13
17
  cookies = {};
@@ -18,11 +22,12 @@ export class Database extends EventEmitter {
18
22
  this.host = host;
19
23
  this.name = conn.database;
20
24
  this.connection_details = conn;
21
- this.debug = !!conn.debug;
25
+ this.debug = conn.debug ?? false;
22
26
  }
27
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
23
28
  generateExternalSourceLogin(data) {
24
- if (data.credentials.method === "filemaker") {
25
- let _data = data.credentials;
29
+ if (data.credentials.method === 'filemaker') {
30
+ const _data = data.credentials;
26
31
  return {
27
32
  database: data.database,
28
33
  username: _data.username,
@@ -30,50 +35,66 @@ export class Database extends EventEmitter {
30
35
  };
31
36
  }
32
37
  else {
33
- throw "Not yet supported login method";
38
+ throw new Error('Not yet supported login method');
34
39
  }
35
40
  }
41
+ /**
42
+ * Logs out the user by deleting the current session token.
43
+ * Throws an error if the user is not logged in.
44
+ *
45
+ * @returns {Promise<void>} A promise that resolves with no value once the logout is successful.
46
+ * @throws {Error} Throws an error if the user is not logged in.
47
+ */
36
48
  async logout() {
37
- if (!this.token)
38
- throw new Error("Not logged in");
39
- let _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
40
- method: "DELETE",
49
+ if (this.token === '')
50
+ throw new Error('Not logged in');
51
+ const _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
52
+ method: 'DELETE',
41
53
  headers: {
42
- "content-type": "application/json"
54
+ 'content-type': 'application/json'
43
55
  }
44
56
  });
45
- let data = await _fetch.json();
57
+ await _fetch.json();
46
58
  // console.log(data)
47
- this._token = null;
59
+ this._token = '';
48
60
  }
61
+ /**
62
+ * Logs in to the database. Not required, as this is often done automatically
63
+ *
64
+ * @param {boolean} [forceLogin=false] - Whether to force login even if already logged in.
65
+ * @throws {Error} - Throws an error if already logged in and forceLogin is false.
66
+ * @throws {FMError} - Throws an FMError if login fails.
67
+ * @return {Promise<string>} - Returns a promise that resolves to the access token upon successful login.
68
+ */
49
69
  async login(forceLogin = false) {
70
+ // console.trace()
50
71
  // Reset cookies
51
72
  this.cookies = {};
52
73
  await this.host.getMetadata();
53
- if (this.token && !forceLogin)
54
- throw new Error("Already logged in. Run logout() first");
55
- if (this.connection_details.credentials.method === "token") {
56
- this._token = this.connection_details.credentials.token;
74
+ if (this.token !== '' && !forceLogin)
75
+ throw new Error('Already logged in. Run logout() first');
76
+ if (this.connection_details.credentials.method === 'token') {
77
+ this._token = (this.connection_details.credentials).token;
57
78
  return this.token;
58
79
  }
59
80
  else {
60
81
  console.time();
61
- let url = new URL(`${this.endpoint}/sessions`);
82
+ const url = new URL(`${this.endpoint}/sessions`);
62
83
  url.hostname = this.host.hostname;
63
- let res = await fetch(url, {
64
- method: "POST",
84
+ const res = await fetch(url, {
85
+ method: 'POST',
65
86
  headers: generateAuthorizationHeaders(this.connection_details.credentials),
66
87
  body: JSON.stringify({
67
88
  fmDataSource: this.connection_details.externalSources.map(i => {
68
- let _i = i;
89
+ const _i = i;
69
90
  return this.generateExternalSourceLogin(_i);
70
91
  })
71
92
  })
72
93
  });
73
94
  console.time();
74
- let _res = (await res.json());
95
+ const _res = (await res.json());
75
96
  if (res.status === 200) {
76
- this._token = res.headers.get('x-fm-data-access-token');
97
+ this._token = res.headers.get('x-fm-data-access-token') ?? '';
77
98
  return this._token;
78
99
  }
79
100
  else {
@@ -84,54 +105,65 @@ export class Database extends EventEmitter {
84
105
  get token() {
85
106
  return this._token;
86
107
  }
108
+ /**
109
+ * Returns the endpoint URL for the database connection.
110
+ *
111
+ * @returns {string} The endpoint URL.
112
+ */
87
113
  get endpoint() {
88
114
  return `${this.host.hostname}/fmi/data/v2/databases/${this.name}`;
89
115
  }
90
- async apiRequestRaw(url, options = {}, autoRelogin = true) {
116
+ async _apiRequestRaw(url, options = {}, autoRelogin = true) {
91
117
  if (this.debug)
92
- console.log(`EASYFM DEBUG: ${options.method?.toString().toUpperCase() || "GET"} ${url instanceof Request ? url.url : url}`);
93
- if (!this.token)
118
+ console.log(`EASYFM DEBUG: ${JSON.stringify(options)} ${url instanceof Request ? url.url : url}`);
119
+ if (this.token === '')
94
120
  await this.login(true);
95
121
  if (!options.headers)
96
122
  options.headers = {};
97
- options.headers["authorization"] = "Bearer " + this._token;
98
- options.rejectUnauthorized = this.host.verify;
99
- let _fetch = await fetch(url, options);
123
+ options.headers.authorization = 'Bearer ' + this._token;
124
+ // options.rejectUnauthorized = this.host.verify
125
+ const _fetch = await fetch(url, options);
100
126
  if (_fetch.headers.get('set-cookie')) {
101
- for (let cookie of _fetch.headers.get('set-cookie') || []) {
102
- let cookie_split = cookie.split("=");
103
- this.cookies[cookie_split[0]] = cookie_split[1];
127
+ for (const cookie of _fetch.headers.get('set-cookie') ?? []) {
128
+ const cookieSplit = cookie.split('=');
129
+ this.cookies[cookieSplit[0]] = cookieSplit[1];
104
130
  }
105
131
  }
106
132
  if (_fetch.status === 401 && autoRelogin) {
107
133
  await this.login(true);
108
- return await this.apiRequestRaw(url, options, false);
134
+ return await this._apiRequestRaw(url, options, false);
109
135
  }
110
136
  return _fetch;
111
137
  }
112
- async apiRequestJSON(url, options = {}) {
138
+ async _apiRequestJSON(url, options = {}) {
113
139
  if (!options.headers)
114
140
  options.headers = {};
115
- options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json";
116
- let _fetch = await this.apiRequestRaw(url, options);
117
- let data = await _fetch.json();
141
+ options.headers['content-type'] = options.headers['content-type'] ? options.headers['content-type'] : 'application/json';
142
+ const _fetch = await this._apiRequestRaw(url, options);
143
+ const data = await _fetch.json();
118
144
  // Remove response if it is empty. This makes checking for an empty response easier
119
145
  if (data.response && Object.keys(data.response).length === 0)
120
146
  delete data.response;
121
147
  // console.log(data.messages[0])
122
- if (data.messages[0].code !== "0") {
148
+ if (data.messages[0].code !== '0') {
123
149
  throw new FMError(data.messages[0].code, _fetch.status, data);
124
150
  }
125
151
  data.httpStatus = _fetch.status;
126
152
  return data;
127
153
  }
154
+ /**
155
+ * Retrieves a list of layouts in the current FileMaker database.
156
+ *
157
+ * @returns {Promise<Layout[]>} A promise that resolves to an array of Layout objects.
158
+ * @throws {FMError} If there was an error retrieving the layouts.
159
+ */
128
160
  async listLayouts() {
129
- let req = await this.apiRequestJSON(`${this.endpoint}/layouts?page=2`);
161
+ const req = await this._apiRequestJSON(`${this.endpoint}/layouts?page=2`);
130
162
  if (!req.response)
131
163
  throw new FMError(req.messages[0].code, req.httpStatus, req.messages[0].message);
132
164
  const cycleLayoutNames = (layouts) => {
133
165
  let names = [];
134
- for (let layout of layouts) {
166
+ for (const layout of layouts) {
135
167
  if (layout.folderLayoutNames)
136
168
  names = names.concat(cycleLayoutNames(layout.folderLayoutNames));
137
169
  else
@@ -139,47 +171,43 @@ export class Database extends EventEmitter {
139
171
  }
140
172
  return names;
141
173
  };
142
- let layoutNames;
143
174
  return cycleLayoutNames(req.response.layouts).map(layout => new Layout(this, layout));
144
175
  }
145
- getLayout(name) {
176
+ layout(name) {
146
177
  return new Layout(this, name);
147
178
  }
148
- script(name, parameter = "") {
149
- return { name, parameter };
150
- }
151
- _tokenExpired() {
152
- this.emit("token_expired");
179
+ script(name, parameter = '') {
180
+ return ({ name, parameter });
153
181
  }
154
- streamURL(url) {
155
- return new Promise((resolve, reject) => {
156
- if (!url || typeof url !== "string") {
157
- throw new Error("URL is empty, or has invalid value");
182
+ async streamURL(url) {
183
+ return await new Promise((resolve, reject) => {
184
+ if (!url || typeof url !== 'string') {
185
+ throw new Error('URL is empty, or has invalid value');
158
186
  }
159
- let headers = {};
187
+ const headers = {};
160
188
  if (Object.keys(this.cookies).length !== 0) {
161
- headers["Cookie"] = Object.keys(this.cookies)
189
+ headers.Cookie = Object.keys(this.cookies)
162
190
  .map(key => {
163
- return key + "=" + this.cookies[key];
191
+ return key + '=' + this.cookies[key];
164
192
  })
165
- .join("; ");
193
+ .join('; ');
166
194
  }
167
- const check_for_cookies = (res) => {
195
+ const checkForCookies = (res) => {
168
196
  // Check for the 'set-cookie' header. If it exists, remember it and strip it for better security.
169
197
  if (res.headers['set-cookie']) {
170
- for (let cookie of res.headers['set-cookie']) {
171
- let cookie_split = cookie.split("=");
172
- this.cookies[cookie_split[0]] = cookie_split[1];
198
+ for (const cookie of res.headers['set-cookie']) {
199
+ const cookieSplit = cookie.split('=');
200
+ this.cookies[cookieSplit[0]] = cookieSplit[1];
173
201
  }
174
202
  res.headers['set-cookie'] = undefined;
175
203
  }
176
204
  };
177
205
  // Automatically switch between the http and https modules, based on which is needed
178
- (url.startsWith("https") ? HTTP_REDIRECT.https : HTTP_REDIRECT.http).get(url, {
206
+ (url.startsWith('https') ? HTTP_REDIRECT.https : HTTP_REDIRECT.http).get(url, {
179
207
  headers,
180
- beforeRedirect: (options, res) => check_for_cookies(res)
208
+ beforeRedirect: (options, res) => { checkForCookies(res); }
181
209
  }, (res) => {
182
- check_for_cookies(res);
210
+ checkForCookies(res);
183
211
  resolve(res);
184
212
  });
185
213
  });