@jd-data-limited/easy-fm 4.1.1 → 4.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jd-data-limited/easy-fm",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
4
4
  "dependencies": {
5
5
  "@types/follow-redirects": "^1.14.4",
6
6
  "follow-redirects": "^1.15.5",
@@ -1,53 +1,10 @@
1
- "use strict";
2
1
  /*
3
2
  * Copyright (c) 2024. See LICENSE file for more information
4
3
  */
5
- var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
6
- if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
7
- return cooked;
8
- };
9
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
10
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
11
- return new (P || (P = Promise))(function (resolve, reject) {
12
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
13
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
14
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
15
- step((generator = generator.apply(thisArg, _arguments || [])).next());
16
- });
17
- };
18
- var __generator = (this && this.__generator) || function (thisArg, body) {
19
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
20
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
21
- function verb(n) { return function (v) { return step([n, v]); }; }
22
- function step(op) {
23
- if (f) throw new TypeError("Generator is already executing.");
24
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
25
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
26
- if (y = 0, t) op = [op[0] & 2, t.value];
27
- switch (op[0]) {
28
- case 0: case 1: t = op; break;
29
- case 4: _.label++; return { value: op[1], done: false };
30
- case 5: _.label++; y = op[1]; op = [0]; continue;
31
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
32
- default:
33
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
34
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
35
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
36
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
37
- if (t[2]) _.ops.pop();
38
- _.trys.pop(); continue;
39
- }
40
- op = body.call(thisArg, _);
41
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
42
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
43
- }
44
- };
45
- Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.generateTypesCLI = void 0;
47
- var apiResults_js_1 = require("../models/apiResults.js");
48
- var inquirer_1 = require("inquirer");
49
- var FMHost_js_1 = require("../connection/FMHost.js");
50
- var fs = require("fs");
4
+ import { ApiFieldResultTypes } from "../models/apiResults.js";
5
+ import inquirer from 'inquirer';
6
+ import FMHost from "../connection/FMHost.js";
7
+ import * as fs from "fs";
51
8
  function sub(str) {
52
9
  return str
53
10
  .replace(/[^a-zA-Z0-9_]/g, "") // Remove non-alphanumeric characters
@@ -55,42 +12,38 @@ function sub(str) {
55
12
  }
56
13
  function generateFieldType(field) {
57
14
  switch (field.result) {
58
- case apiResults_js_1.ApiFieldResultTypes.TEXT:
15
+ case ApiFieldResultTypes.TEXT:
59
16
  return "Field<string>";
60
- case apiResults_js_1.ApiFieldResultTypes.CONTAINER:
17
+ case ApiFieldResultTypes.CONTAINER:
61
18
  return "Field<Container>";
62
- case apiResults_js_1.ApiFieldResultTypes.DATE:
19
+ case ApiFieldResultTypes.DATE:
63
20
  return "Field<Date>";
64
- case apiResults_js_1.ApiFieldResultTypes.NUMBER:
21
+ case ApiFieldResultTypes.NUMBER:
65
22
  return "Field<number>";
66
- case apiResults_js_1.ApiFieldResultTypes.TIME:
23
+ case ApiFieldResultTypes.TIME:
67
24
  return "Field<Date>";
68
- case apiResults_js_1.ApiFieldResultTypes.TIMESTAMP:
25
+ case ApiFieldResultTypes.TIMESTAMP:
69
26
  return "Field<Date>";
70
27
  }
71
28
  }
72
29
  function processFieldsChunk(fields) {
73
- var existing_fields = [];
74
- fields = fields.filter(function (field) {
30
+ let existing_fields = [];
31
+ fields = fields.filter(field => {
75
32
  if (existing_fields.includes(field.name))
76
33
  return false;
77
34
  existing_fields.push(field.name);
78
35
  return true;
79
36
  });
80
- return fields.map(function (field) {
37
+ return fields.map(field => {
81
38
  return JSON.stringify(field.name) + ": " + generateFieldType(field);
82
39
  });
83
40
  }
84
41
  function camelToSnakeCase(str) {
85
42
  return str.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
86
43
  }
87
- function safeStringInjection(str) {
88
- var args = [];
89
- for (var _i = 1; _i < arguments.length; _i++) {
90
- args[_i - 1] = arguments[_i];
91
- }
92
- var result = "";
93
- for (var i = 0; i < str.length; i++) {
44
+ function safeStringInjection(str, ...args) {
45
+ let result = "";
46
+ for (let i = 0; i < str.length; i++) {
94
47
  result += str[i];
95
48
  if (i < args.length) {
96
49
  result += JSON.stringify(args[i].toString());
@@ -98,158 +51,111 @@ function safeStringInjection(str) {
98
51
  }
99
52
  return result;
100
53
  }
101
- function generateTypesCLI() {
102
- return __awaiter(this, void 0, void 0, function () {
103
- var data, HOST, DATABASE, stream, layouts, layoutInterfaces, _loop_1, _i, layouts_1, layout, layoutInterfaceLinks, _a, layoutInterfaces_1, layout, _b, layoutInterfaces_2, layout;
104
- return __generator(this, function (_c) {
105
- switch (_c.label) {
106
- case 0:
107
- process.stdout.write('\x1Bc');
108
- console.log("For this tool to work, you are required to have access to an account on the target database.\n" +
109
- "This account must have the fmrest extended privilege enabled.\n" +
110
- "This tool can only export type interfaces for layouts that the given account has access to.");
111
- console.log("\n");
112
- return [4 /*yield*/, inquirer_1.default.prompt([
113
- {
114
- type: "input",
115
- name: "hostname",
116
- message: "The IP address/domain of your FileMaker server (must start with either http:// or https://)",
117
- validate: function (input) {
118
- return input.startsWith("http://") || input.startsWith("https://") || "Please enter a valid IP address/domain starting with http:// or https://";
119
- }
120
- },
121
- {
122
- type: "number",
123
- name: "timezoneOffset",
124
- message: "Server timezone offset (minutes)",
125
- default: 0 - (new Date()).getTimezoneOffset()
126
- },
127
- {
128
- type: "confirm",
129
- name: "verify",
130
- default: true,
131
- message: "Verify your server's identity",
132
- when: function (previousAnswers) {
133
- return previousAnswers.hostname.startsWith("https://");
134
- }
135
- },
136
- {
137
- type: "input",
138
- name: "database",
139
- message: "Database filename"
140
- },
141
- {
142
- type: "input",
143
- name: "username",
144
- message: "Database username"
145
- },
146
- {
147
- type: "password",
148
- name: "password",
149
- message: "Database password",
150
- mask: "*"
151
- }
152
- ])];
153
- case 1:
154
- data = _c.sent();
155
- HOST = new FMHost_js_1.default(data.hostname, data.timezoneOffset, data.verify);
156
- DATABASE = HOST.database({
157
- database: data.database,
158
- credentials: {
159
- method: "filemaker",
160
- username: data.username,
161
- password: data.password
162
- },
163
- externalSources: []
164
- });
165
- return [4 /*yield*/, DATABASE.login()
166
- // Create file write stream
167
- ];
168
- case 2:
169
- _c.sent();
170
- stream = fs.createWriteStream("./types.ts");
171
- return [4 /*yield*/, DATABASE.listLayouts()];
172
- case 3:
173
- layouts = _c.sent();
174
- stream.write("import FMHost, {Container, FieldBase, LayoutInterface, Portal} from \"@jd-data-limited/easy-fm\";\n");
175
- stream.write("// " + layouts.length + " layouts found\n\n");
176
- layoutInterfaces = new Map();
177
- _loop_1 = function (layout) {
178
- var substituteName, metadata, fields, portals, portalsProcessed, interfaceName;
179
- return __generator(this, function (_d) {
180
- switch (_d.label) {
181
- case 0:
182
- substituteName = sub(layout.name);
183
- if (!substituteName)
184
- return [2 /*return*/, "continue"];
185
- console.log(substituteName);
186
- return [4 /*yield*/, layout.getLayoutMeta()];
187
- case 1:
188
- metadata = _d.sent();
189
- fields = processFieldsChunk(metadata.fieldMetaData);
190
- portals = metadata.portalMetaData;
191
- portalsProcessed = Object.keys(portals).map(function (portalName) {
192
- return "".concat(JSON.stringify(portalName), ": Portal<{").concat(processFieldsChunk(portals[portalName]), "}>");
193
- });
194
- interfaceName = substituteName + "LayoutInterface";
195
- layoutInterfaces.set(layout.name, {
196
- interfaceName: interfaceName,
197
- interface: "export interface ".concat(interfaceName, " extends LayoutInterface {\n fields: {").concat(fields.join(",\n "), "},\n portals: {").concat(portalsProcessed.join(",\n "), "}\n }")
198
- });
199
- return [2 /*return*/];
200
- }
201
- });
202
- };
203
- _i = 0, layouts_1 = layouts;
204
- _c.label = 4;
205
- case 4:
206
- if (!(_i < layouts_1.length)) return [3 /*break*/, 7];
207
- layout = layouts_1[_i];
208
- return [5 /*yield**/, _loop_1(layout)];
209
- case 5:
210
- _c.sent();
211
- _c.label = 6;
212
- case 6:
213
- _i++;
214
- return [3 /*break*/, 4];
215
- case 7:
216
- layoutInterfaceLinks = [];
217
- for (_a = 0, layoutInterfaces_1 = layoutInterfaces; _a < layoutInterfaces_1.length; _a++) {
218
- layout = layoutInterfaces_1[_a];
219
- layoutInterfaceLinks.push([
220
- "".concat(JSON.stringify(layout[0]), ": ").concat(layout[1].interfaceName),
221
- "".concat(sub(camelToSnakeCase(layout[0]).toUpperCase().replace(" ", "_")), ": HOST.getLayout<").concat(layout[1].interfaceName, ">(").concat(JSON.stringify(layout[0]), ")")
222
- ]);
223
- }
224
- stream.write(safeStringInjection(templateObject_1 || (templateObject_1 = __makeTemplateObject(["const HOST = new FMHost(", ", ", ", ", ");"], ["const HOST = new FMHost(", ", ", ", ", ");"])), data.hostname, data.timezoneOffset, data.verify));
225
- stream.write("HOST.database<{layouts:{".concat(layoutInterfaceLinks.map(function (i) { return i[0]; }).join(","), "}}>();"));
226
- stream.write("\n\n");
227
- stream.write("const LAYOUTS = {".concat(layoutInterfaceLinks.map(function (i) { return i[1]; }).join(","), "};"));
228
- for (_b = 0, layoutInterfaces_2 = layoutInterfaces; _b < layoutInterfaces_2.length; _b++) {
229
- layout = layoutInterfaces_2[_b];
230
- stream.write("\n\n");
231
- stream.write(layout[1].interface + ";");
232
- }
233
- // StructureKind
234
- //
235
- // const project = new Project({
236
- // tsConfigFilePath: "../tsconfig.json"
237
- // })
238
- // project.resolveSourceFileDependencies()
239
- return [4 /*yield*/, DATABASE.logout()];
240
- case 8:
241
- // StructureKind
242
- //
243
- // const project = new Project({
244
- // tsConfigFilePath: "../tsconfig.json"
245
- // })
246
- // project.resolveSourceFileDependencies()
247
- _c.sent();
248
- console.log("DONE! Wrote database types to ./types.ts");
249
- return [2 /*return*/];
54
+ export async function generateTypesCLI() {
55
+ process.stdout.write('\x1Bc');
56
+ console.log("For this tool to work, you are required to have access to an account on the target database.\n" +
57
+ "This account must have the fmrest extended privilege enabled.\n" +
58
+ "This tool can only export type interfaces for layouts that the given account has access to.");
59
+ console.log("\n");
60
+ const data = await inquirer.prompt([
61
+ {
62
+ type: "input",
63
+ name: "hostname",
64
+ message: "The IP address/domain of your FileMaker server (must start with either http:// or https://)",
65
+ validate(input) {
66
+ return input.startsWith("http://") || input.startsWith("https://") || "Please enter a valid IP address/domain starting with http:// or https://";
250
67
  }
251
- });
68
+ },
69
+ {
70
+ type: "number",
71
+ name: "timezoneOffset",
72
+ message: "Server timezone offset (minutes)",
73
+ default: 0 - (new Date()).getTimezoneOffset()
74
+ },
75
+ {
76
+ type: "confirm",
77
+ name: "verify",
78
+ default: true,
79
+ message: "Verify your server's identity",
80
+ when(previousAnswers) {
81
+ return previousAnswers.hostname.startsWith("https://");
82
+ }
83
+ },
84
+ {
85
+ type: "input",
86
+ name: "database",
87
+ message: "Database filename"
88
+ },
89
+ {
90
+ type: "input",
91
+ name: "username",
92
+ message: "Database username"
93
+ },
94
+ {
95
+ type: "password",
96
+ name: "password",
97
+ message: "Database password",
98
+ mask: "*"
99
+ }
100
+ ]);
101
+ const HOST = new FMHost(data.hostname, data.timezoneOffset, data.verify);
102
+ const DATABASE = HOST.database({
103
+ database: data.database,
104
+ credentials: {
105
+ method: "filemaker",
106
+ username: data.username,
107
+ password: data.password
108
+ },
109
+ externalSources: []
252
110
  });
111
+ await DATABASE.login();
112
+ // Create file write stream
113
+ const stream = fs.createWriteStream("./types.ts");
114
+ let layouts = await DATABASE.listLayouts();
115
+ stream.write(`import FMHost, {Container, FieldBase, LayoutInterface, Portal} from "@jd-data-limited/easy-fm";\n`);
116
+ stream.write("// " + layouts.length + " layouts found\n\n");
117
+ let layoutInterfaces = new Map();
118
+ for (let layout of layouts) {
119
+ let substituteName = sub(layout.name);
120
+ if (!substituteName)
121
+ continue;
122
+ console.log(substituteName);
123
+ let metadata = await layout.getLayoutMeta();
124
+ let fields = processFieldsChunk(metadata.fieldMetaData);
125
+ let portals = metadata.portalMetaData;
126
+ let portalsProcessed = Object.keys(portals).map(portalName => {
127
+ return `${JSON.stringify(portalName)}: Portal<{${processFieldsChunk(portals[portalName])}}>`;
128
+ });
129
+ let interfaceName = substituteName + "LayoutInterface";
130
+ layoutInterfaces.set(layout.name, {
131
+ interfaceName,
132
+ interface: `export interface ${interfaceName} extends LayoutInterface {
133
+ fields: {${fields.join(",\n ")}},
134
+ portals: {${portalsProcessed.join(",\n ")}}
135
+ }`
136
+ });
137
+ }
138
+ let layoutInterfaceLinks = [];
139
+ for (let layout of layoutInterfaces) {
140
+ layoutInterfaceLinks.push([
141
+ `${JSON.stringify(layout[0])}: ${layout[1].interfaceName}`,
142
+ `${sub(camelToSnakeCase(layout[0]).toUpperCase().replace(" ", "_"))}: HOST.getLayout<${layout[1].interfaceName}>(${JSON.stringify(layout[0])})`
143
+ ]);
144
+ }
145
+ stream.write(safeStringInjection `const HOST = new FMHost(${data.hostname}, ${data.timezoneOffset}, ${data.verify});`);
146
+ stream.write(`HOST.database<{layouts:{${layoutInterfaceLinks.map(i => i[0]).join(",")}}}>();`);
147
+ stream.write("\n\n");
148
+ stream.write(`const LAYOUTS = {${layoutInterfaceLinks.map(i => i[1]).join(",")}};`);
149
+ for (let layout of layoutInterfaces) {
150
+ stream.write("\n\n");
151
+ stream.write(layout[1].interface + ";");
152
+ }
153
+ // StructureKind
154
+ //
155
+ // const project = new Project({
156
+ // tsConfigFilePath: "../tsconfig.json"
157
+ // })
158
+ // project.resolveSourceFileDependencies()
159
+ await DATABASE.logout();
160
+ console.log("DONE! Wrote database types to ./types.ts");
253
161
  }
254
- exports.generateTypesCLI = generateTypesCLI;
255
- var templateObject_1;
@@ -7,7 +7,7 @@ import { type HostBase } from './HostBase.js';
7
7
  import { type DatabaseBase } from './databaseBase.js';
8
8
  import { type ApiResults } from '../models/apiResults.js';
9
9
  import { type DatabaseStructure } from '../databaseStructure.js';
10
- import { RequestInfo, RequestInit, Response } from 'node-fetch';
10
+ import { type RequestInfo, type RequestInit, type Response } from 'node-fetch';
11
11
  /**
12
12
  * Represents a database connection.
13
13
  * @template T - The structure of the database.
@@ -6,8 +6,8 @@ import { generateAuthorizationHeaders } from './generateAuthorizationHeaders.js'
6
6
  import { FMError } from '../FMError.js';
7
7
  import { Layout } from '../layouts/layout.js';
8
8
  import fetch from 'node-fetch';
9
- // @ts-ignore
10
- import fetchWithCookies, { CookieJar } from "node-fetch-cookies";
9
+ // @ts-expect-error
10
+ import fetchWithCookies, { CookieJar } from 'node-fetch-cookies';
11
11
  /**
12
12
  * Represents a database connection.
13
13
  * @template T - The structure of the database.
@@ -57,7 +57,6 @@ export class Database extends EventEmitter {
57
57
  }
58
58
  });
59
59
  await _fetch.json();
60
- // console.log(data)
61
60
  this._token = '';
62
61
  }
63
62
  /**
@@ -71,8 +70,6 @@ export class Database extends EventEmitter {
71
70
  async login(forceLogin = false) {
72
71
  if (this.token !== '' && !forceLogin)
73
72
  return;
74
- console.log("RELOGGING IN");
75
- console.trace();
76
73
  // Reset cookies
77
74
  this.cookies = new CookieJar();
78
75
  await this.host.getMetadata();
@@ -81,7 +78,6 @@ export class Database extends EventEmitter {
81
78
  return this.token;
82
79
  }
83
80
  else {
84
- console.time();
85
81
  const url = new URL(`${this.endpoint}/sessions`);
86
82
  url.hostname = this.host.hostname;
87
83
  const res = await fetch(url, {
@@ -94,7 +90,6 @@ export class Database extends EventEmitter {
94
90
  })
95
91
  })
96
92
  });
97
- console.time();
98
93
  const _res = (await res.json());
99
94
  if (res.status === 200) {
100
95
  this._token = res.headers.get('x-fm-data-access-token') ?? '';
@@ -128,9 +123,9 @@ export class Database extends EventEmitter {
128
123
  // options.headers.cookies = this._generateCookieHeader()
129
124
  // console.log(options.headers.cookies)
130
125
  // options.rejectUnauthorized = this.host.verify
131
- const _fetch = options.useCookieJar ?
132
- await fetchWithCookies(this.cookies, url, options) :
133
- await fetch(url, options);
126
+ const _fetch = options.useCookieJar
127
+ ? await fetchWithCookies(this.cookies, url, options)
128
+ : await fetch(url, options);
134
129
  // if (_fetch.headers.get('set-cookie')) {
135
130
  // console.log(_fetch.headers.get('set-cookie'))
136
131
  // for (const cookie of (_fetch.headers.get("Set-Cookie") ?? "").split("; ")) {
@@ -151,11 +146,11 @@ export class Database extends EventEmitter {
151
146
  return _fetch;
152
147
  }
153
148
  _generateCookieHeader() {
154
- let out = [];
155
- for (let key of Object.keys(this.cookies)) {
149
+ const out = [];
150
+ for (const key of Object.keys(this.cookies)) {
156
151
  out.push(`${key}=${this.cookies[key]}`);
157
152
  }
158
- return out.join("; ");
153
+ return out.join('; ');
159
154
  }
160
155
  async _apiRequestJSON(url, options = {}) {
161
156
  if (!options.headers)
@@ -12,9 +12,9 @@ import {type HostBase} from './HostBase.js'
12
12
  import {type DatabaseBase} from './databaseBase.js'
13
13
  import {type ApiLayout, type ApiResults} from '../models/apiResults.js'
14
14
  import {type DatabaseStructure} from '../databaseStructure.js'
15
- import fetch, {HeadersInit, RequestInfo, RequestInit, Response} from 'node-fetch';
16
- // @ts-ignore
17
- import fetchWithCookies, {CookieJar} from "node-fetch-cookies"
15
+ import fetch, {type HeadersInit, type RequestInfo, type RequestInit, type Response} from 'node-fetch'
16
+ // @ts-expect-error
17
+ import fetchWithCookies, {CookieJar} from 'node-fetch-cookies'
18
18
 
19
19
  /**
20
20
  * Represents a database connection.
@@ -28,7 +28,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
28
28
  readonly name: string
29
29
  readonly debug: boolean
30
30
 
31
- constructor(host: HostBase, conn: databaseOptionsWithExternalSources) {
31
+ constructor (host: HostBase, conn: databaseOptionsWithExternalSources) {
32
32
  super()
33
33
  this.host = host
34
34
  this.name = conn.database
@@ -37,7 +37,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
37
37
  }
38
38
 
39
39
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
40
- private generateExternalSourceLogin(data: databaseOptionsBase) {
40
+ private generateExternalSourceLogin (data: databaseOptionsBase) {
41
41
  if (data.credentials.method === 'filemaker') {
42
42
  const _data = data.credentials
43
43
  return {
@@ -45,8 +45,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
45
45
  username: _data.username,
46
46
  password: _data.password
47
47
  }
48
- }
49
- else {
48
+ } else {
50
49
  throw new Error('Not yet supported login method')
51
50
  }
52
51
  }
@@ -58,7 +57,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
58
57
  * @returns {Promise<void>} A promise that resolves with no value once the logout is successful.
59
58
  * @throws {Error} Throws an error if the user is not logged in.
60
59
  */
61
- async logout(): Promise<void> {
60
+ async logout (): Promise<void> {
62
61
  if (this.token === '') throw new Error('Not logged in')
63
62
 
64
63
  const _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
@@ -68,7 +67,6 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
68
67
  }
69
68
  })
70
69
  await _fetch.json()
71
- // console.log(data)
72
70
  this._token = ''
73
71
  }
74
72
 
@@ -80,11 +78,9 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
80
78
  * @throws {FMError} - Throws an FMError if login fails.
81
79
  * @return {Promise<string>} - Returns a promise that resolves to the access token upon successful login.
82
80
  */
83
- async login(forceLogin = false) {
81
+ async login (forceLogin = false) {
84
82
  if (this.token !== '' && !forceLogin) return
85
83
 
86
- console.log("RELOGGING IN")
87
- console.trace()
88
84
  // Reset cookies
89
85
  this.cookies = new CookieJar()
90
86
 
@@ -93,9 +89,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
93
89
  if (this.connection_details.credentials.method === 'token') {
94
90
  this._token = (this.connection_details.credentials).token
95
91
  return this.token
96
- }
97
- else {
98
- console.time()
92
+ } else {
99
93
  const url = new URL(`${this.endpoint}/sessions`)
100
94
  url.hostname = this.host.hostname
101
95
  const res = await fetch(url, {
@@ -108,13 +102,11 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
108
102
  })
109
103
  })
110
104
  })
111
- console.time()
112
105
  const _res = (await res.json()) as any
113
106
  if (res.status === 200) {
114
107
  this._token = res.headers.get('x-fm-data-access-token') ?? ''
115
108
  return this._token
116
- }
117
- else {
109
+ } else {
118
110
  throw new FMError(
119
111
  _res.messages[0].code as string | number,
120
112
  _res.status as number,
@@ -124,7 +116,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
124
116
  }
125
117
  }
126
118
 
127
- get token() {
119
+ get token () {
128
120
  return this._token
129
121
  }
130
122
 
@@ -133,13 +125,13 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
133
125
  *
134
126
  * @returns {string} The endpoint URL.
135
127
  */
136
- get endpoint(): string {
128
+ get endpoint (): string {
137
129
  return `${this.host.hostname}/fmi/data/v2/databases/${this.name}`
138
130
  }
139
131
 
140
- async _apiRequestRaw(url: URL | RequestInfo, options: RequestInit & {
141
- headers?: Record<string, string>,
142
- useCookieJar?: boolean,
132
+ async _apiRequestRaw (url: URL | RequestInfo, options: RequestInit & {
133
+ headers?: Record<string, string>
134
+ useCookieJar?: boolean
143
135
  } = {}, autoRelogin = true): Promise<Response> {
144
136
  if (this.debug) console.log(`EASYFM DEBUG: ${JSON.stringify(options)} ${url instanceof Request ? url.url : url}`)
145
137
  if (this.token === '') await this.login(true)
@@ -152,9 +144,9 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
152
144
  // options.rejectUnauthorized = this.host.verify
153
145
 
154
146
  const _fetch: Response =
155
- options.useCookieJar ?
156
- await fetchWithCookies(this.cookies, url, options):
157
- await fetch(url, options)
147
+ options.useCookieJar
148
+ ? await fetchWithCookies(this.cookies, url, options)
149
+ : await fetch(url, options)
158
150
  // if (_fetch.headers.get('set-cookie')) {
159
151
  // console.log(_fetch.headers.get('set-cookie'))
160
152
  // for (const cookie of (_fetch.headers.get("Set-Cookie") ?? "").split("; ")) {
@@ -170,16 +162,15 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
170
162
  console.trace()
171
163
  await this.login(true)
172
164
  return await this._apiRequestRaw(url, options, false)
173
- }
174
- else return _fetch
165
+ } else return _fetch
175
166
  }
176
167
 
177
- private _generateCookieHeader() {
178
- let out: string[] = []
179
- for (let key of Object.keys(this.cookies)) {
168
+ private _generateCookieHeader () {
169
+ const out: string[] = []
170
+ for (const key of Object.keys(this.cookies)) {
180
171
  out.push(`${key}=${this.cookies[key]}`)
181
172
  }
182
- return out.join("; ")
173
+ return out.join('; ')
183
174
  }
184
175
 
185
176
  async _apiRequestJSON<T = any>(url: URL | RequestInfo, options: RequestInit & {
@@ -208,7 +199,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
208
199
  * @returns {Promise<Layout[]>} A promise that resolves to an array of Layout objects.
209
200
  * @throws {FMError} If there was an error retrieving the layouts.
210
201
  */
211
- async listLayouts() {
202
+ async listLayouts () {
212
203
  const req = await this._apiRequestJSON<{ layouts: ApiLayout[] }>(`${this.endpoint}/layouts?page=2`)
213
204
  if (!req.response) throw new FMError(req.messages[0].code, req.httpStatus, req.messages[0].message)
214
205
 
@@ -225,11 +216,11 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
225
216
 
226
217
  layout<R extends keyof T['layouts']>(name: R): Layout<T['layouts'][R]>
227
218
  layout<R extends LayoutInterface>(name: string): Layout<R>
228
- layout(name: string): Layout<any> {
219
+ layout (name: string): Layout<any> {
229
220
  return new Layout<LayoutInterface>(this, name)
230
221
  }
231
222
 
232
- script(name: string, parameter = ''): Script {
223
+ script (name: string, parameter = ''): Script {
233
224
  return ({name, parameter} satisfies Script)
234
225
  }
235
226
  }
@@ -1,6 +1,6 @@
1
1
  import { type HostBase } from './HostBase.js';
2
2
  import { type ApiResults } from '../models/apiResults.js';
3
- import { RequestInfo, RequestInit, Response } from "node-fetch";
3
+ import { type RequestInfo, type RequestInit, type Response } from 'node-fetch';
4
4
  export interface DatabaseBase {
5
5
  host: HostBase;
6
6
  readonly name: string;
@@ -4,7 +4,7 @@
4
4
 
5
5
  import {type HostBase} from './HostBase.js'
6
6
  import {type ApiResults} from '../models/apiResults.js'
7
- import {RequestInfo, RequestInit, Response} from "node-fetch";
7
+ import {type RequestInfo, type RequestInit, type Response} from 'node-fetch'
8
8
 
9
9
  export interface DatabaseBase {
10
10
  host: HostBase
@@ -41,7 +41,7 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
41
41
  };
42
42
  import { RecordTypes } from '../types.js';
43
43
  import { ApiFieldDisplayTypes, ApiFieldResultTypes, ApiFieldTypes } from '../models/apiResults.js';
44
- import { FMError } from "../FMError.js";
44
+ import { FMError } from '../FMError.js';
45
45
  /**
46
46
  * A class representing a field in a record.
47
47
  *
@@ -58,7 +58,7 @@ let Field = (() => {
58
58
  _private_streamAsync_decorators = [containerDownloadFunction];
59
59
  _arrayBuffer_decorators = [containerDownloadFunction];
60
60
  __esDecorate(this, _private_streamAsync_descriptor = { value: __setFunctionName(async function () {
61
- let req = await this.parent.layout.database._apiRequestRaw(this.string, { useCookieJar: true });
61
+ const req = await this.parent.layout.database._apiRequestRaw(this.string, { useCookieJar: true });
62
62
  if (!req.ok || !req.body) {
63
63
  throw new Error(`HTTP Error: ${req.status} (${req.statusText})`);
64
64
  }
@@ -178,7 +178,6 @@ let Field = (() => {
178
178
  }
179
179
  const form = new FormData();
180
180
  form.append('upload', file);
181
- console.log(`ENDPOINT: ${this.parent.endpoint}/containers/${this.id}/1`);
182
181
  const res = await this.parent.layout.database._apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
183
182
  method: 'POST',
184
183
  headers: {
@@ -188,27 +187,23 @@ let Field = (() => {
188
187
  body: form
189
188
  });
190
189
  if (!res.ok) {
191
- console.log("RESULT:", await res.text());
192
190
  throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`);
193
191
  }
194
192
  const data = await res.json();
195
- if (data.messages[0].code === "0")
193
+ if (data.messages[0].code === '0')
196
194
  return;
197
195
  else {
198
- console.log(data);
199
196
  throw new FMError(data.messages[0].code, res.status, res);
200
197
  }
201
198
  }
202
199
  get #streamAsync() { return _private_streamAsync_descriptor.value; }
203
- stream() {
204
- return this.#streamAsync();
200
+ async stream() {
201
+ return await this.#streamAsync();
205
202
  }
206
203
  async arrayBuffer() {
207
- console.log(this.string);
208
204
  const req = await this.parent.layout.database._apiRequestRaw(this.string, { useCookieJar: true });
209
205
  if (!req.ok)
210
206
  throw new Error(`HTTP Error: ${req.status} (${req.statusText})`);
211
- console.log(req);
212
207
  return await req.arrayBuffer();
213
208
  }
214
209
  };
@@ -216,16 +211,15 @@ let Field = (() => {
216
211
  export { Field };
217
212
  function containerDownloadFunction(originalMethod, context) {
218
213
  async function replacementMethod(...args) {
219
- return ContainerDownloadExecutor.processTask(() => originalMethod.call(this, ...args));
214
+ return await ContainerDownloadExecutor.processTask(async () => await originalMethod.call(this, ...args));
220
215
  }
221
216
  return replacementMethod;
222
217
  }
223
218
  class ContainerDownloadExecutorClass {
224
219
  hasDownloadedFirstContainer = 0;
225
220
  waitingFuncs = [];
226
- constructor() { }
227
221
  async processTask(task) {
228
- let isFirstTask = this.hasDownloadedFirstContainer === 0;
222
+ const isFirstTask = this.hasDownloadedFirstContainer === 0;
229
223
  if (isFirstTask) {
230
224
  this.hasDownloadedFirstContainer = 1;
231
225
  let res;
@@ -234,7 +228,7 @@ class ContainerDownloadExecutorClass {
234
228
  }
235
229
  catch (e) {
236
230
  this.hasDownloadedFirstContainer = 0;
237
- let nextTask = this.waitingFuncs.splice(0, 1)[0];
231
+ const nextTask = this.waitingFuncs.splice(0, 1)[0];
238
232
  if (nextTask) {
239
233
  this.processTask(nextTask.func)
240
234
  .then(res => nextTask.done(res))
@@ -243,7 +237,7 @@ class ContainerDownloadExecutorClass {
243
237
  throw e;
244
238
  }
245
239
  this.hasDownloadedFirstContainer = 2;
246
- for (let task of this.waitingFuncs) {
240
+ for (const task of this.waitingFuncs) {
247
241
  task.func()
248
242
  .then((res) => task.done(res))
249
243
  .catch((e) => task.err(e));
@@ -251,7 +245,7 @@ class ContainerDownloadExecutorClass {
251
245
  return res;
252
246
  }
253
247
  else if (this.hasDownloadedFirstContainer === 1) {
254
- return new Promise((resolve, reject) => {
248
+ return await new Promise((resolve, reject) => {
255
249
  this.waitingFuncs.push({
256
250
  func: task,
257
251
  done: resolve,
@@ -6,7 +6,7 @@ import {RecordTypes} from '../types.js'
6
6
  import {ApiFieldDisplayTypes, type ApiFieldMetadata, ApiFieldResultTypes, ApiFieldTypes} from '../models/apiResults.js'
7
7
  import {type LayoutBase} from '../layouts/layoutBase.js'
8
8
  import {type Moment} from 'moment'
9
- import {FMError} from "../FMError.js";
9
+ import {FMError} from '../FMError.js'
10
10
 
11
11
  export type FieldValue = string | number | Moment | Container
12
12
  export type Container = null
@@ -31,7 +31,7 @@ export class Field<T extends FieldValue> {
31
31
 
32
32
  static firstContainerDownload: Promise<any> | null = null
33
33
 
34
- constructor(record: Parentable, id: string, contents: T) {
34
+ constructor (record: Parentable, id: string, contents: T) {
35
35
  this.parent = record
36
36
  this.id = id
37
37
  this._value = contents
@@ -44,13 +44,13 @@ export class Field<T extends FieldValue> {
44
44
  * @param {T | null} content - The new content value to be set. Can be either the type T or null.
45
45
  * @throws {Error} Cannot set container value using set(). Use upload() instead, if the result is a 'container'.
46
46
  */
47
- set(content: T | null) {
47
+ set (content: T | null) {
48
48
  if (this.metadata.result === 'container') throw new Error('Cannot set container value using set(). Use upload() instead.')
49
49
  else this._value = content
50
50
  this.edited = true
51
51
  }
52
52
 
53
- get metadata(): ApiFieldMetadata {
53
+ get metadata (): ApiFieldMetadata {
54
54
  if (!this.parent.layout.metadata) {
55
55
  // Default to a regular text field
56
56
  return {
@@ -88,8 +88,7 @@ export class Field<T extends FieldValue> {
88
88
  repetitions: 1,
89
89
  valueList: ''
90
90
  }
91
- }
92
- else {
91
+ } else {
93
92
  return this.parent.layout.metadata.fieldMetaData.find(i => i.name === this.id) ?? {
94
93
  name: this.id.toString(),
95
94
  type: ApiFieldTypes.NORMAL,
@@ -109,16 +108,16 @@ export class Field<T extends FieldValue> {
109
108
  }
110
109
  }
111
110
 
112
- get value(): T | null {
111
+ get value (): T | null {
113
112
  // if (this.metadata.result === "container") throw "Use await field.stream() to get the contents of a container field, instead of field.value"
114
113
  return this._value
115
114
  }
116
115
 
117
- set value(value: T) {
116
+ set value (value: T) {
118
117
  this._value = value
119
118
  }
120
119
 
121
- get string(): string {
120
+ get string (): string {
122
121
  if (typeof this._value === 'string') {
123
122
  return this._value
124
123
  }
@@ -135,14 +134,13 @@ export class Field<T extends FieldValue> {
135
134
  *
136
135
  * @returns {Promise<void>} - A promise that resolves when the file is successfully uploaded.
137
136
  */
138
- async upload(file: File): Promise<void> {
137
+ async upload (file: File): Promise<void> {
139
138
  if (this.metadata.result !== 'container') {
140
139
  throw new Error('Cannot upload a file to the field; ' + this.id + ' (not a container field)')
141
140
  }
142
141
  const form = new FormData()
143
142
  form.append('upload', file)
144
143
 
145
- console.log(`ENDPOINT: ${this.parent.endpoint}/containers/${this.id}/1`)
146
144
  const res = await this.parent.layout.database._apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
147
145
  method: 'POST',
148
146
  headers: {
@@ -152,43 +150,39 @@ export class Field<T extends FieldValue> {
152
150
  body: form
153
151
  })
154
152
  if (!res.ok) {
155
- console.log("RESULT:", await res.text())
156
153
  throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`)
157
154
  }
158
155
  const data: { messages: Array<{ code: string }> } = await res.json() as any
159
- if (data.messages[0].code === "0") return
156
+ if (data.messages[0].code === '0') return
160
157
  else {
161
- console.log(data)
162
158
  throw new FMError(data.messages[0].code, res.status, res)
163
159
  }
164
160
  }
165
161
 
166
162
  @containerDownloadFunction
167
- async #streamAsync() {
168
- let req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
163
+ async #streamAsync () {
164
+ const req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
169
165
  if (!req.ok || !req.body) {
170
166
  throw new Error(`HTTP Error: ${req.status} (${req.statusText})`)
171
167
  }
172
168
  return req.body
173
169
  }
174
170
 
175
- stream() {
176
- return this.#streamAsync()
171
+ async stream () {
172
+ return await this.#streamAsync()
177
173
  }
178
174
 
179
175
  @containerDownloadFunction
180
- async arrayBuffer() {
181
- console.log(this.string)
176
+ async arrayBuffer () {
182
177
  const req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
183
178
  if (!req.ok) throw new Error(`HTTP Error: ${req.status} (${req.statusText})`)
184
- console.log(req)
185
179
  return await req.arrayBuffer()
186
180
  }
187
181
  }
188
182
 
189
- function containerDownloadFunction<T extends FieldValue, R = unknown>(originalMethod: (...p: any[]) => Promise<R>, context: ClassMethodDecoratorContext<Field<T>>) {
190
- async function replacementMethod(this: Field<T>, ...args: any[]) {
191
- return ContainerDownloadExecutor.processTask(() => originalMethod.call(this, ...args))
183
+ function containerDownloadFunction<PROPS extends any[], T extends FieldValue, R = unknown> (originalMethod: (...p: PROPS) => Promise<R>, context: ClassMethodDecoratorContext<Field<T>>) {
184
+ async function replacementMethod (this: Field<T>, ...args: PROPS) {
185
+ return await ContainerDownloadExecutor.processTask(async () => await originalMethod.call(this, ...args))
192
186
  }
193
187
 
194
188
  return replacementMethod
@@ -196,24 +190,22 @@ function containerDownloadFunction<T extends FieldValue, R = unknown>(originalMe
196
190
 
197
191
  class ContainerDownloadExecutorClass {
198
192
  hasDownloadedFirstContainer: 0 | 1 | 2 = 0
199
- waitingFuncs: {
200
- func: (() => any),
201
- done: ((...args: any[]) => any),
202
- err: ((e: Error) => any)
203
- }[] = []
204
-
205
- constructor() {}
193
+ waitingFuncs: Array<{
194
+ func: (() => any)
195
+ done: ((...args: any[]) => any)
196
+ err: ((e: any) => any)
197
+ }> = []
206
198
 
207
199
  async processTask<T extends () => Promise<any>>(task: T) {
208
- let isFirstTask = this.hasDownloadedFirstContainer === 0
200
+ const isFirstTask = this.hasDownloadedFirstContainer === 0
209
201
  if (isFirstTask) {
210
202
  this.hasDownloadedFirstContainer = 1
211
203
  let res
212
204
  try {
213
205
  res = await task()
214
- } catch(e) {
206
+ } catch (e) {
215
207
  this.hasDownloadedFirstContainer = 0
216
- let nextTask = this.waitingFuncs.splice(0, 1)[0]
208
+ const nextTask = this.waitingFuncs.splice(0, 1)[0]
217
209
  if (nextTask) {
218
210
  this.processTask(nextTask.func)
219
211
  .then(res => nextTask.done(res))
@@ -222,24 +214,22 @@ class ContainerDownloadExecutorClass {
222
214
  throw e
223
215
  }
224
216
  this.hasDownloadedFirstContainer = 2
225
- for (let task of this.waitingFuncs) {
217
+ for (const task of this.waitingFuncs) {
226
218
  task.func()
227
219
  .then((res: any) => task.done(res))
228
220
  .catch((e: any) => task.err(e))
229
221
  }
230
222
  return res
231
- }
232
- else if (this.hasDownloadedFirstContainer === 1) {
233
- return new Promise((resolve, reject) => {
223
+ } else if (this.hasDownloadedFirstContainer === 1) {
224
+ return await new Promise((resolve, reject) => {
234
225
  this.waitingFuncs.push({
235
226
  func: task,
236
227
  done: resolve,
237
228
  err: reject
238
229
  })
239
230
  })
240
- }
241
- else return await task()
231
+ } else return await task()
242
232
  }
243
233
  }
244
234
 
245
- const ContainerDownloadExecutor = new ContainerDownloadExecutorClass()
235
+ const ContainerDownloadExecutor = new ContainerDownloadExecutorClass()
@@ -135,7 +135,6 @@ export class RecordGetOperation {
135
135
  ? this.layout.database.host.timeFormat
136
136
  : this.layout.database.host.timeStampFormat);
137
137
  }).join('');
138
- console.log(this.layout.database.host.timeStampFormat, out[key]);
139
138
  }
140
139
  return out;
141
140
  }
@@ -165,7 +165,6 @@ export class RecordGetOperation<T extends LayoutInterface, OPTIONS extends GetOp
165
165
  : this.layout.database.host.timeStampFormat
166
166
  )
167
167
  }).join('')
168
- console.log(this.layout.database.host.timeStampFormat, out[key])
169
168
  }
170
169
  return out
171
170
  }
@@ -153,7 +153,6 @@ export class LayoutRecord extends RecordBase {
153
153
  const res = await this.layout.database._apiRequestJSON(this.endpoint, {
154
154
  method: 'DELETE'
155
155
  });
156
- console.log(res);
157
156
  if (typeof res.response?.scriptError !== 'undefined' && res.response?.scriptError !== '0') {
158
157
  throw new FMError(res.response.scriptError, res.httpStatus, res);
159
158
  }
@@ -177,7 +177,6 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
177
177
  const res = await this.layout.database._apiRequestJSON(this.endpoint, {
178
178
  method: 'DELETE'
179
179
  })
180
- console.log(res)
181
180
  if (typeof res.response?.scriptError !== 'undefined' && res.response?.scriptError !== '0') {
182
181
  throw new FMError(res.response.scriptError, res.httpStatus, res)
183
182
  } else if (res.messages[0].code === '0') {