@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
package/src/bin/cli.js CHANGED
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
+ "use strict";
2
3
  /*
3
4
  * Copyright (c) 2024. See LICENSE file for more information
4
5
  */
5
- import { Command } from "commander";
6
- import { generateTypesCLI } from "./generateTypes.js";
7
- const program = new Command();
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ var commander_1 = require("commander");
8
+ var generateTypes_js_1 = require("./generateTypes.js");
9
+ var program = new commander_1.Command();
8
10
  program
9
11
  .name("easyfm")
10
12
  .description("A NodeJS wrapper for the FileMaker Data CLI");
11
13
  program
12
14
  .command("generate-types")
13
15
  .description("Automatically generate layout interfaces from your database")
14
- .action(() => {
15
- generateTypesCLI();
16
+ .action(function () {
17
+ (0, generateTypes_js_1.generateTypesCLI)();
16
18
  });
17
19
  program.parse(process.argv);
@@ -1,10 +1,53 @@
1
+ "use strict";
1
2
  /*
2
3
  * Copyright (c) 2024. See LICENSE file for more information
3
4
  */
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";
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");
8
51
  function sub(str) {
9
52
  return str
10
53
  .replace(/[^a-zA-Z0-9_]/g, "") // Remove non-alphanumeric characters
@@ -12,38 +55,42 @@ function sub(str) {
12
55
  }
13
56
  function generateFieldType(field) {
14
57
  switch (field.result) {
15
- case ApiFieldResultTypes.TEXT:
58
+ case apiResults_js_1.ApiFieldResultTypes.TEXT:
16
59
  return "Field<string>";
17
- case ApiFieldResultTypes.CONTAINER:
60
+ case apiResults_js_1.ApiFieldResultTypes.CONTAINER:
18
61
  return "Field<Container>";
19
- case ApiFieldResultTypes.DATE:
62
+ case apiResults_js_1.ApiFieldResultTypes.DATE:
20
63
  return "Field<Date>";
21
- case ApiFieldResultTypes.NUMBER:
64
+ case apiResults_js_1.ApiFieldResultTypes.NUMBER:
22
65
  return "Field<number>";
23
- case ApiFieldResultTypes.TIME:
66
+ case apiResults_js_1.ApiFieldResultTypes.TIME:
24
67
  return "Field<Date>";
25
- case ApiFieldResultTypes.TIMESTAMP:
68
+ case apiResults_js_1.ApiFieldResultTypes.TIMESTAMP:
26
69
  return "Field<Date>";
27
70
  }
28
71
  }
29
72
  function processFieldsChunk(fields) {
30
- let existing_fields = [];
31
- fields = fields.filter(field => {
73
+ var existing_fields = [];
74
+ fields = fields.filter(function (field) {
32
75
  if (existing_fields.includes(field.name))
33
76
  return false;
34
77
  existing_fields.push(field.name);
35
78
  return true;
36
79
  });
37
- return fields.map(field => {
80
+ return fields.map(function (field) {
38
81
  return JSON.stringify(field.name) + ": " + generateFieldType(field);
39
82
  });
40
83
  }
41
84
  function camelToSnakeCase(str) {
42
85
  return str.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
43
86
  }
44
- function safeStringInjection(str, ...args) {
45
- let result = "";
46
- for (let i = 0; i < str.length; i++) {
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++) {
47
94
  result += str[i];
48
95
  if (i < args.length) {
49
96
  result += JSON.stringify(args[i].toString());
@@ -51,111 +98,158 @@ function safeStringInjection(str, ...args) {
51
98
  }
52
99
  return result;
53
100
  }
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://";
67
- }
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://");
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*/];
82
250
  }
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: []
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
251
  });
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");
252
+ });
161
253
  }
254
+ exports.generateTypesCLI = generateTypesCLI;
255
+ var templateObject_1;
@@ -1,15 +1,37 @@
1
- import { Database } from "./database.js";
2
- import { HostBase } from "./HostBase.js";
3
- import { databaseOptionsWithExternalSources, FMHostMetadata, loginOptionsClaris, loginOptionsFileMaker, loginOptionsOAuth } from "../types.js";
4
- import { DatabaseStructure } from "../databaseStructure.js";
1
+ import { Database } from './database.js';
2
+ import { type HostBase } from './HostBase.js';
3
+ import { type databaseOptionsWithExternalSources, type FMHostMetadata, type loginOptionsClaris, type loginOptionsFileMaker, type loginOptionsOAuth } from '../types.js';
4
+ import { type DatabaseStructure } from '../databaseStructure.js';
5
+ import { type Moment } from 'moment';
6
+ /**
7
+ * Represents a FileMaker host.
8
+ * @implements {HostBase}
9
+ */
5
10
  export default class FMHost implements HostBase {
6
11
  readonly hostname: string;
7
- readonly timezoneOffset: number;
12
+ readonly timezoneOffsetFunc: (moment: Moment) => number;
8
13
  readonly verify: boolean;
9
14
  _metadata: FMHostMetadata | null;
10
- constructor(_hostname: string, timezoneOffset?: number, verify?: boolean);
15
+ constructor(_hostname: string, timezoneOffset?: (moment: Moment) => number, verify?: boolean);
11
16
  get metadata(): FMHostMetadata;
17
+ get dateFormat(): string;
18
+ get timeFormat(): string;
19
+ get timeStampFormat(): string;
20
+ /**
21
+ * Retrieves a list of databases from the FileMaker Server.
22
+ *
23
+ * @param {loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris} [credentials] - Optional credentials required for authentication.
24
+ * @throws {FMError} If the request to the FileMaker Server fails or if the response contains an error.
25
+ * @returns {Promise<any[]>} A promise that resolves to an array of database objects if successful.
26
+ */
12
27
  listDatabases(credentials?: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris): Promise<any>;
28
+ /**
29
+ * Creates a new database connection with the specified options.
30
+ *
31
+ * @template T - The type of the database structure.
32
+ * @param {databaseOptionsWithExternalSources} data - The options for the database, including external sources.
33
+ * @return {Database<T>} A new Database instance.
34
+ */
13
35
  database<T extends DatabaseStructure>(data: databaseOptionsWithExternalSources): Database<T>;
14
36
  getMetadata(): Promise<FMHostMetadata>;
15
37
  }
@@ -1,70 +1,97 @@
1
1
  /*
2
2
  * Copyright (c) 2023-2024. See LICENSE file for more information
3
3
  */
4
- import { generateAuthorizationHeaders } from "./generateAuthorizationHeaders.js";
5
- import { FMError } from "../FMError.js";
6
- import { Database } from "./database.js";
4
+ import { generateAuthorizationHeaders } from './generateAuthorizationHeaders.js';
5
+ import { FMError } from '../FMError.js';
6
+ import { Database } from './database.js';
7
+ /**
8
+ * Represents a FileMaker host.
9
+ * @implements {HostBase}
10
+ */
7
11
  export default class FMHost {
8
12
  hostname;
9
- timezoneOffset;
13
+ timezoneOffsetFunc;
10
14
  verify;
11
15
  _metadata = null;
12
- constructor(_hostname, timezoneOffset = 0 - (new Date()).getTimezoneOffset(), verify = true) {
16
+ constructor(_hostname, timezoneOffset = (moment) => 0 - (new Date()).getTimezoneOffset(), verify = true) {
13
17
  if (!(/^https?:\/\//).test(_hostname))
14
- throw "hostname MUST begin with either http:// or https://";
18
+ throw new Error('hostname MUST begin with either http:// or https://');
15
19
  this.hostname = _hostname;
16
- this.timezoneOffset = timezoneOffset;
20
+ this.timezoneOffsetFunc = timezoneOffset;
17
21
  this.verify = verify;
18
22
  }
19
23
  get metadata() {
20
- return this._metadata || {
24
+ return this._metadata ?? {
21
25
  productInfo: {
22
26
  buildDate: new Date(),
23
- name: "",
24
- version: "",
25
- dateFormat: "",
26
- timeFormat: "",
27
- timeStampFormat: ""
27
+ name: '',
28
+ version: '',
29
+ dateFormat: 'MM/dd/yyyy',
30
+ timeFormat: 'HH:mm:ss',
31
+ timeStampFormat: 'MM/dd/yyyy HH:mm:ss'
28
32
  }
29
33
  };
30
34
  }
35
+ get dateFormat() {
36
+ return this.metadata.productInfo.dateFormat
37
+ .replace('dd', 'DD')
38
+ .replace('yyyy', 'YYYY');
39
+ }
40
+ get timeFormat() { return this.metadata.productInfo.timeFormat; }
41
+ get timeStampFormat() {
42
+ return this.metadata.productInfo.timeStampFormat
43
+ .replace('dd', 'DD')
44
+ .replace('yyyy', 'YYYY');
45
+ }
46
+ /**
47
+ * Retrieves a list of databases from the FileMaker Server.
48
+ *
49
+ * @param {loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris} [credentials] - Optional credentials required for authentication.
50
+ * @throws {FMError} If the request to the FileMaker Server fails or if the response contains an error.
51
+ * @returns {Promise<any[]>} A promise that resolves to an array of database objects if successful.
52
+ */
31
53
  async listDatabases(credentials) {
32
54
  let headers = {};
33
55
  if (credentials) {
34
56
  headers = generateAuthorizationHeaders(credentials);
35
57
  }
36
- let _fetch = await fetch(`${this.hostname}/fmi/data/v2/databases`, {
37
- method: "GET",
58
+ const _fetch = await fetch(`${this.hostname}/fmi/data/v2/databases`, {
59
+ method: 'GET',
38
60
  headers
39
61
  });
40
- let data = await _fetch.json();
62
+ const data = await _fetch.json();
41
63
  // console.log(data.messages[0])
42
- if (data.messages[0].code === "0" && data.response) {
64
+ if (data.messages[0].code === '0' && data.response) {
43
65
  return data.response.databases;
44
66
  }
45
67
  else {
46
- // @ts-ignore
47
- throw new FMError(data.messages[0].code, data.status, data);
68
+ throw new FMError(data.messages[0].code, _fetch.status, data);
48
69
  }
49
70
  }
71
+ /**
72
+ * Creates a new database connection with the specified options.
73
+ *
74
+ * @template T - The type of the database structure.
75
+ * @param {databaseOptionsWithExternalSources} data - The options for the database, including external sources.
76
+ * @return {Database<T>} A new Database instance.
77
+ */
50
78
  database(data) {
51
79
  return new Database(this, data);
52
80
  }
53
81
  async getMetadata() {
54
82
  if (this._metadata)
55
83
  return this._metadata;
56
- let _fetch = await fetch(`${this.hostname}/fmi/data/v2/productInfo`, {
57
- method: "GET",
84
+ const _fetch = await fetch(`${this.hostname}/fmi/data/v2/productInfo`, {
85
+ method: 'GET'
58
86
  });
59
- let data = await _fetch.json();
87
+ const data = await _fetch.json();
60
88
  // console.log(data.messages[0])
61
- if (data.messages[0].code === "0" && data.response) {
89
+ if (data.messages[0].code === '0' && data.response) {
62
90
  this._metadata = data.response;
63
91
  return data.response;
64
92
  }
65
93
  else {
66
- // @ts-ignore
67
- throw new FMError(data.messages[0].code, data.status, data);
94
+ throw new FMError(data.messages[0].code, _fetch.status, data);
68
95
  }
69
96
  }
70
97
  }