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

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 (64) hide show
  1. package/package.json +16 -7
  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/connection/FMHost.d.ts +23 -5
  6. package/src/connection/FMHost.js +42 -26
  7. package/src/connection/FMHost.ts +63 -49
  8. package/src/connection/HostBase.d.ts +3 -3
  9. package/src/connection/HostBase.ts +5 -5
  10. package/src/connection/database.d.ts +53 -20
  11. package/src/connection/database.js +107 -90
  12. package/src/connection/database.ts +133 -121
  13. package/src/connection/databaseBase.d.ts +11 -7
  14. package/src/connection/databaseBase.ts +8 -8
  15. package/src/connection/generateAuthorizationHeaders.d.ts +1 -1
  16. package/src/connection/generateAuthorizationHeaders.js +10 -10
  17. package/src/connection/generateAuthorizationHeaders.ts +21 -21
  18. package/src/databaseStructure.d.ts +2 -4
  19. package/src/databaseStructure.ts +3 -5
  20. package/src/index.d.ts +17 -17
  21. package/src/index.js +13 -13
  22. package/src/index.ts +24 -24
  23. package/src/layouts/layout.d.ts +17 -6
  24. package/src/layouts/layout.js +19 -8
  25. package/src/layouts/layout.ts +33 -23
  26. package/src/layouts/layoutBase.d.ts +5 -5
  27. package/src/layouts/layoutBase.ts +6 -6
  28. package/src/layouts/layoutInterface.d.ts +3 -5
  29. package/src/layouts/layoutInterface.ts +4 -6
  30. package/src/layouts/layoutRecordManager.d.ts +29 -6
  31. package/src/layouts/layoutRecordManager.js +32 -9
  32. package/src/layouts/layoutRecordManager.ts +42 -19
  33. package/src/layouts/recordFieldsMap.d.ts +2 -4
  34. package/src/layouts/recordFieldsMap.ts +2 -4
  35. package/src/models/apiResults.d.ts +7 -13
  36. package/src/models/apiResults.ts +58 -58
  37. package/src/records/field.d.ts +39 -17
  38. package/src/records/field.js +248 -131
  39. package/src/records/field.ts +160 -82
  40. package/src/records/getOperations/recordGetOperation.d.ts +44 -24
  41. package/src/records/getOperations/recordGetOperation.js +88 -61
  42. package/src/records/getOperations/recordGetOperation.ts +128 -108
  43. package/src/records/layoutRecord.d.ts +28 -14
  44. package/src/records/layoutRecord.js +69 -60
  45. package/src/records/layoutRecord.ts +121 -119
  46. package/src/records/layoutRecordBase.d.ts +2 -2
  47. package/src/records/layoutRecordBase.ts +3 -3
  48. package/src/records/portal.d.ts +19 -5
  49. package/src/records/portal.js +19 -5
  50. package/src/records/portal.ts +28 -16
  51. package/src/records/portalBase.d.ts +5 -5
  52. package/src/records/portalBase.ts +6 -6
  53. package/src/records/portalRecord.d.ts +16 -8
  54. package/src/records/portalRecord.js +16 -6
  55. package/src/records/portalRecord.ts +29 -19
  56. package/src/records/recordBase.d.ts +18 -8
  57. package/src/records/recordBase.js +23 -16
  58. package/src/records/recordBase.ts +41 -37
  59. package/src/types.d.ts +23 -30
  60. package/src/types.js +0 -5
  61. package/src/types.ts +55 -64
  62. package/src/utils/query.d.ts +7 -7
  63. package/src/utils/query.js +10 -10
  64. package/src/utils/query.ts +19 -20
@@ -2,21 +2,34 @@
2
2
  * Copyright (c) 2023-2024. See LICENSE file for more information
3
3
  */
4
4
 
5
- import * as http from "http";
6
- import {ContainerBufferResult, DownloadModes, FieldMetaData, RecordTypes} from "../types.js";
7
- import {ApiFieldDisplayTypes, ApiFieldMetadata, ApiFieldResultTypes, ApiFieldTypes} from "../models/apiResults.js";
8
- import {LayoutBase} from "../layouts/layoutBase.js";
9
- import {Moment} from "moment";
5
+ import {RecordTypes} from '../types.js'
6
+ import {ApiFieldDisplayTypes, type ApiFieldMetadata, ApiFieldResultTypes, ApiFieldTypes} from '../models/apiResults.js'
7
+ import {type LayoutBase} from '../layouts/layoutBase.js'
8
+ import {type Moment} from 'moment'
9
+ import {FMError} from "../FMError.js";
10
10
 
11
11
  export type FieldValue = string | number | Moment | Container
12
12
  export type Container = null
13
13
 
14
- export type Parentable = {layout: LayoutBase, type: RecordTypes, endpoint: string}
14
+ export interface Parentable {
15
+ layout: LayoutBase
16
+ type: RecordTypes
17
+ endpoint: string
18
+ portal?: { name: string }
19
+ }
20
+
21
+ /**
22
+ * A class representing a field in a record.
23
+ *
24
+ * @template T - The type of the field value.
25
+ */
15
26
  export class Field<T extends FieldValue> {
16
- parent: Parentable;
17
- id: string;
18
- protected _value: T;
19
- edited: boolean;
27
+ parent: Parentable
28
+ id: string
29
+ protected _value: T | null
30
+ edited: boolean
31
+
32
+ static firstContainerDownload: Promise<any> | null = null
20
33
 
21
34
  constructor(record: Parentable, id: string, contents: T) {
22
35
  this.parent = record
@@ -25,11 +38,14 @@ export class Field<T extends FieldValue> {
25
38
  this.edited = false
26
39
  }
27
40
 
28
- set(content: T) {
29
- if (this.metadata.result === "container") throw "Cannot set container value using set(). Use upload() instead."
30
- // @ts-ignore
31
- if (!content) this._value = ""
32
- // @ts-ignore
41
+ /**
42
+ * Sets the value of the field.
43
+ *
44
+ * @param {T | null} content - The new content value to be set. Can be either the type T or null.
45
+ * @throws {Error} Cannot set container value using set(). Use upload() instead, if the result is a 'container'.
46
+ */
47
+ set(content: T | null) {
48
+ if (this.metadata.result === 'container') throw new Error('Cannot set container value using set(). Use upload() instead.')
33
49
  else this._value = content
34
50
  this.edited = true
35
51
  }
@@ -51,30 +67,30 @@ export class Field<T extends FieldValue> {
51
67
  numeric: false,
52
68
  timeOfDay: false,
53
69
  repetitions: 1,
54
- valueList: ""
70
+ valueList: ''
55
71
  }
56
72
  }
57
- if (this.parent.type === RecordTypes.PORTAL) {
58
- // @ts-ignore
59
- return this.parent.layout.metadata.portalMetaData[this.parent.portal.name || "portal not attached"].find(i => i.name === this.id) as FieldMetaData || {
60
- name: this.id.toString(),
61
- type: 'normal',
62
- displayType: 'editText',
63
- result: 'text',
64
- global: false,
65
- autoEnter: true,
66
- fourDigitYear: false,
67
- maxRepeat: 1,
68
- maxCharacters: 0,
69
- notEmpty: false,
70
- numeric: false,
71
- timeOfDay: false,
72
- repetitionStart: 1,
73
- repetitionEnd: 1
74
- } as FieldMetaData
73
+ if (this.parent.type === RecordTypes.PORTAL && this.parent.portal) {
74
+ return this.parent.layout.metadata.portalMetaData[this.parent.portal.name || 'portal not attached'].find(i => i.name === this.id) ??
75
+ {
76
+ name: this.id.toString(),
77
+ type: ApiFieldTypes.NORMAL,
78
+ displayType: ApiFieldDisplayTypes.EDIT_TEXT,
79
+ result: ApiFieldResultTypes.TEXT,
80
+ global: false,
81
+ autoEnter: true,
82
+ fourDigitYear: false,
83
+ maxRepeat: 1,
84
+ maxCharacters: 0,
85
+ notEmpty: false,
86
+ numeric: false,
87
+ timeOfDay: false,
88
+ repetitions: 1,
89
+ valueList: ''
90
+ }
75
91
  }
76
92
  else {
77
- return this.parent.layout.metadata.fieldMetaData.find(i => i.name === this.id) || {
93
+ return this.parent.layout.metadata.fieldMetaData.find(i => i.name === this.id) ?? {
78
94
  name: this.id.toString(),
79
95
  type: ApiFieldTypes.NORMAL,
80
96
  displayType: ApiFieldDisplayTypes.EDIT_TEXT,
@@ -88,12 +104,12 @@ export class Field<T extends FieldValue> {
88
104
  numeric: false,
89
105
  timeOfDay: false,
90
106
  repetitions: 1,
91
- valueList: ""
107
+ valueList: ''
92
108
  }
93
109
  }
94
110
  }
95
111
 
96
- get value(): T {
112
+ get value(): T | null {
97
113
  // if (this.metadata.result === "container") throw "Use await field.stream() to get the contents of a container field, instead of field.value"
98
114
  return this._value
99
115
  }
@@ -103,65 +119,127 @@ export class Field<T extends FieldValue> {
103
119
  }
104
120
 
105
121
  get string(): string {
106
- if (typeof this._value === "string") {
122
+ if (typeof this._value === 'string') {
107
123
  return this._value
108
124
  }
109
- throw "Field value is not a string"
125
+ throw new Error('Field value is not a string')
110
126
  }
111
127
 
112
- get date(): Date {
113
- if (this._value instanceof Date) {
114
- return this._value
128
+ /**
129
+ * Uploads a file to the container field.
130
+ *
131
+ * @param {Buffer} file - The file content as a buffer.
132
+ *
133
+ * @throws {Error} - Cannot upload a file to the field if it's not a container field.
134
+ * @throws {Error} - Upload failed with HTTP error.
135
+ *
136
+ * @returns {Promise<void>} - A promise that resolves when the file is successfully uploaded.
137
+ */
138
+ async upload(file: File): Promise<void> {
139
+ if (this.metadata.result !== 'container') {
140
+ throw new Error('Cannot upload a file to the field; ' + this.id + ' (not a container field)')
141
+ }
142
+ const form = new FormData()
143
+ form.append('upload', file)
144
+
145
+ console.log(`ENDPOINT: ${this.parent.endpoint}/containers/${this.id}/1`)
146
+ const res = await this.parent.layout.database._apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
147
+ method: 'POST',
148
+ headers: {
149
+ // Authorization: 'Bearer ' + this.parent.layout.database.token,
150
+ // 'Content-Type': 'multipart/form-data'
151
+ },
152
+ body: form
153
+ })
154
+ if (!res.ok) {
155
+ console.log("RESULT:", await res.text())
156
+ throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`)
157
+ }
158
+ const data: { messages: Array<{ code: string }> } = await res.json() as any
159
+ if (data.messages[0].code === "0") return
160
+ else {
161
+ console.log(data)
162
+ throw new FMError(data.messages[0].code, res.status, res)
115
163
  }
116
- throw "Field value is not a date"
117
164
  }
118
165
 
119
- get number(): number {
120
- if (typeof this._value === "number") {
121
- return this._value
166
+ @containerDownloadFunction
167
+ async #streamAsync() {
168
+ let req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
169
+ if (!req.ok || !req.body) {
170
+ throw new Error(`HTTP Error: ${req.status} (${req.statusText})`)
122
171
  }
123
- throw "Field value is not a number"
172
+ return req.body
124
173
  }
125
174
 
126
- async upload(buffer: Buffer, filename: string, mime: string): Promise<void> {
127
- let trace = new Error()
128
- if (this.metadata.result !== "container") throw "Cannot upload a file to the field; " + this.id + " (not a container field)"
129
- let form = new FormData()
130
- form.append("upload", new File([buffer], filename, {type: mime}))
175
+ stream() {
176
+ return this.#streamAsync()
177
+ }
131
178
 
132
- let res = await this.parent.layout.database.apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
133
- method: "POST",
134
- // @ts-ignore
135
- body: form
136
- })
137
- if (!res.ok) throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`)
179
+ @containerDownloadFunction
180
+ async arrayBuffer() {
181
+ console.log(this.string)
182
+ const req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
183
+ if (!req.ok) throw new Error(`HTTP Error: ${req.status} (${req.statusText})`)
184
+ console.log(req)
185
+ return await req.arrayBuffer()
138
186
  }
187
+ }
139
188
 
140
- download(mode: DownloadModes.Stream): Promise<http.IncomingMessage>
141
- download(mode: DownloadModes.Buffer): Promise<ContainerBufferResult>
142
- async download(mode: DownloadModes = DownloadModes.Stream): Promise<http.IncomingMessage | ContainerBufferResult> {
143
- if (this.metadata.result !== ApiFieldResultTypes.CONTAINER) throw new Error("Cannot perform download() on a non-container field")
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))
192
+ }
144
193
 
145
- let stream = await this.parent.layout.database.streamURL(this.string)
146
- if (mode === DownloadModes.Stream) {
147
- return stream
194
+ return replacementMethod
195
+ }
196
+
197
+ class ContainerDownloadExecutorClass {
198
+ hasDownloadedFirstContainer: 0 | 1 | 2 = 0
199
+ waitingFuncs: {
200
+ func: (() => any),
201
+ done: ((...args: any[]) => any),
202
+ err: ((e: Error) => any)
203
+ }[] = []
204
+
205
+ constructor() {}
206
+
207
+ async processTask<T extends () => Promise<any>>(task: T) {
208
+ let isFirstTask = this.hasDownloadedFirstContainer === 0
209
+ if (isFirstTask) {
210
+ this.hasDownloadedFirstContainer = 1
211
+ let res
212
+ try {
213
+ res = await task()
214
+ } catch(e) {
215
+ this.hasDownloadedFirstContainer = 0
216
+ let nextTask = this.waitingFuncs.splice(0, 1)[0]
217
+ if (nextTask) {
218
+ this.processTask(nextTask.func)
219
+ .then(res => nextTask.done(res))
220
+ .catch(e => nextTask.err(e))
221
+ }
222
+ throw e
223
+ }
224
+ this.hasDownloadedFirstContainer = 2
225
+ for (let task of this.waitingFuncs) {
226
+ task.func()
227
+ .then((res: any) => task.done(res))
228
+ .catch((e: any) => task.err(e))
229
+ }
230
+ return res
148
231
  }
149
-
150
- let body: Uint8Array[] = []
151
- return new Promise((resolve, reject) => {
152
- stream.on("data", chunk => {
153
- body.push(chunk)
154
- })
155
- stream.on("error", (e) => {
156
- reject(e)
157
- })
158
- stream.on("end", () => {
159
- resolve({
160
- buffer: Buffer.concat(body),
161
- mime: stream.headers["content-type"],
162
- request: stream
163
- } as ContainerBufferResult)
232
+ else if (this.hasDownloadedFirstContainer === 1) {
233
+ return new Promise((resolve, reject) => {
234
+ this.waitingFuncs.push({
235
+ func: task,
236
+ done: resolve,
237
+ err: reject
238
+ })
164
239
  })
165
- })
240
+ }
241
+ else return await task()
166
242
  }
167
- }
243
+ }
244
+
245
+ const ContainerDownloadExecutor = new ContainerDownloadExecutorClass()
@@ -1,54 +1,74 @@
1
- import { LayoutInterface } from "../../layouts/layoutInterface.js";
2
- import { PickPortals, ScriptRequestData } from "../../types.js";
3
- import { LayoutBase } from "../../layouts/layoutBase.js";
4
- import { LayoutRecord } from "../layoutRecord.js";
5
- import { Query } from "../../utils/query.js";
6
- export type SortOrder = "ascend" | "descend";
7
- export type FindRequestRaw = {
8
- [key: string]: string;
9
- };
10
- export type FindRequest = {
11
- [key: string]: Query;
12
- };
13
- export type PortalRequest = {
1
+ import { type LayoutInterface } from '../../layouts/layoutInterface.js';
2
+ import { type PickPortals, type ScriptRequestData } from '../../types.js';
3
+ import { type LayoutBase } from '../../layouts/layoutBase.js';
4
+ import { LayoutRecord } from '../layoutRecord.js';
5
+ import { type Query } from '../../utils/query.js';
6
+ export type SortOrder = 'ascend' | 'descend';
7
+ export type FindRequestRaw = Record<string, string>;
8
+ export type FindRequest = Record<string, Query>;
9
+ export interface PortalRequest {
14
10
  name: string;
15
- };
11
+ }
16
12
  type PortalData<T extends LayoutInterface> = {
17
- [key in keyof T["portals"]]: {
13
+ [key in keyof T['portals']]: {
18
14
  limit: number;
19
15
  offset: number;
20
16
  };
21
17
  };
22
- export type GetOperationOptions<T extends LayoutInterface> = {
18
+ export interface GetOperationOptions<T extends LayoutInterface> {
23
19
  portals: Partial<PortalData<T>>;
24
20
  limit?: number;
25
21
  offset?: number;
26
- };
22
+ }
27
23
  export declare class RecordGetOperation<T extends LayoutInterface, OPTIONS extends GetOperationOptions<T>> {
28
24
  protected layout: LayoutBase;
29
25
  protected limit: number;
30
26
  protected scriptData: ScriptRequestData;
31
- protected sortData: {
27
+ protected sortData: Array<{
32
28
  fieldName: string;
33
29
  sortOrder: SortOrder;
34
- }[];
30
+ }>;
35
31
  protected portals: Partial<PortalData<T>>;
36
32
  protected offset: number;
37
- protected queries: {
33
+ protected queries: Array<{
38
34
  req: FindRequestRaw;
39
35
  omit: boolean;
40
- }[];
36
+ }>;
41
37
  constructor(layout: LayoutBase, options: OPTIONS);
42
38
  get isFindRequest(): boolean;
43
39
  private formatQueries;
44
- protected generateParamsBody(offset: number, limit: number): {
45
- [key: string]: any;
46
- };
40
+ protected generateParamsBody(offset: number, limit: number): Record<string, any>;
47
41
  protected generateParamsURL(offset: number, limit: number): URLSearchParams;
42
+ /**
43
+ * Configures any FileMaker scripts to be run as a part of the request
44
+ *
45
+ * @param {ScriptRequestData} scripts - The script request data to set.
46
+ * @return {this} - The current instance of the class.
47
+ */
48
48
  scripts(scripts: ScriptRequestData): this;
49
+ /**
50
+ * Sorts the data based on the given field name and sort order.
51
+ *
52
+ * @param {string} fieldName - The name of the field by which the data should be sorted.
53
+ * @param {SortOrder} sortOrder - The sort order to be applied (either "asc" for ascending or "desc" for descending).
54
+ *
55
+ * @return {this} - Returns the current instance of the object.
56
+ */
49
57
  sort(fieldName: string, sortOrder: SortOrder): this;
50
58
  private parseFindRequest;
59
+ /**
60
+ * Adds a new request/query to the list of queries.
61
+ *
62
+ * @param {FindRequest} query - The find request to be added.
63
+ * @param {boolean} [omit=false] - Flag to indicate if the find request should be omitted.
64
+ * @return {Object} - The current object instance.
65
+ */
51
66
  addRequest(query: FindRequest, omit?: boolean): this;
67
+ /**
68
+ * Perform a fetch operation.
69
+ *
70
+ * @returns {Promise} A promise that resolves with the result of the fetch operation.
71
+ */
52
72
  fetch(): Promise<LayoutRecord<PickPortals<T, keyof OPTIONS["portals"]>>[]>;
53
73
  private performFind;
54
74
  [Symbol.asyncIterator](): {
@@ -1,9 +1,9 @@
1
1
  /*
2
2
  * Copyright (c) 2023-2024. See LICENSE file for more information
3
3
  */
4
- import { LayoutRecord } from "../layoutRecord.js";
5
- import { FMError } from "../../FMError.js";
6
- import { FindRequestSymbol } from "../../utils/query.js";
4
+ import { LayoutRecord } from '../layoutRecord.js';
5
+ import { FMError } from '../../FMError.js';
6
+ import { FindRequestSymbol } from '../../utils/query.js';
7
7
  export class RecordGetOperation {
8
8
  layout;
9
9
  limit = 100;
@@ -16,16 +16,16 @@ export class RecordGetOperation {
16
16
  this.layout = layout;
17
17
  this.sortData = [];
18
18
  this.portals = options.portals;
19
- this.offset = options.offset || 1; // Offset refers to the starting record. offset 1 is the same as no offset.
20
- this.limit = options.limit || 100;
19
+ this.offset = options.offset ?? 1; // Offset refers to the starting record. offset 1 is the same as no offset.
20
+ this.limit = options.limit ?? 100;
21
21
  }
22
22
  get isFindRequest() {
23
23
  return this.queries.length !== 0;
24
24
  }
25
25
  formatQueries() {
26
- let test = this.queries.map(query => {
27
- let out = {};
28
- for (let key of Object.keys(query.req)) {
26
+ const test = this.queries.map(query => {
27
+ const out = {};
28
+ for (const key of Object.keys(query.req)) {
29
29
  if (query.req[key])
30
30
  out[key] = query.req[key];
31
31
  else {
@@ -33,7 +33,7 @@ export class RecordGetOperation {
33
33
  }
34
34
  }
35
35
  if (query.omit)
36
- out.omit = "true";
36
+ out.omit = 'true';
37
37
  return out;
38
38
  });
39
39
  return test;
@@ -47,22 +47,22 @@ export class RecordGetOperation {
47
47
  if (this.sortData.length !== 0)
48
48
  params.sort = this.sortData;
49
49
  if (this.scriptData.after)
50
- params["script"] = this.scriptData.after.name;
50
+ params.script = this.scriptData.after.name;
51
51
  if (this.scriptData.after?.parameter)
52
- params["script.param"] = this.scriptData.after.parameter;
52
+ params['script.param'] = this.scriptData.after.parameter;
53
53
  if (this.scriptData.presort)
54
- params["script.presort"] = this.scriptData.presort.name;
54
+ params['script.presort'] = this.scriptData.presort.name;
55
55
  if (this.scriptData.presort?.parameter)
56
- params["script.presort.param"] = this.scriptData.presort.parameter;
56
+ params['script.presort.param'] = this.scriptData.presort.parameter;
57
57
  if (this.scriptData.prerequest)
58
- params["script.prerequest"] = this.scriptData.prerequest.name;
58
+ params['script.prerequest'] = this.scriptData.prerequest.name;
59
59
  if (this.scriptData.prerequest?.parameter)
60
- params["script.prerequest.param"] = this.scriptData.prerequest.parameter;
60
+ params['script.prerequest.param'] = this.scriptData.prerequest.parameter;
61
61
  if (this.queries.length !== 0)
62
- params["query"] = this.formatQueries();
63
- let portals = Object.keys(this.portals);
64
- params["portal"] = portals;
65
- for (let portal of portals) {
62
+ params.query = this.formatQueries();
63
+ const portals = Object.keys(this.portals);
64
+ params.portal = portals;
65
+ for (const portal of portals) {
66
66
  params[`offset.${portal.toString()}`] = this.portals[portal]?.offset;
67
67
  params[`limit.${portal.toString()}`] = this.portals[portal]?.limit;
68
68
  }
@@ -72,80 +72,107 @@ export class RecordGetOperation {
72
72
  const params = new URLSearchParams({
73
73
  _limit: limit.toString(),
74
74
  _offset: offset.toString(),
75
- dateformats: "2" // Ensure dates are received in ISO8601 format
75
+ dateformats: '2' // Ensure dates are received in ISO8601 format
76
76
  });
77
77
  if (this.sortData.length !== 0)
78
- params.set("_sort", JSON.stringify(this.sortData));
78
+ params.set('_sort', JSON.stringify(this.sortData));
79
79
  if (this.scriptData.after)
80
- params.set("script", this.scriptData.after.name);
80
+ params.set('script', this.scriptData.after.name);
81
81
  if (this.scriptData.after?.parameter)
82
- params.set("script.param", this.scriptData.after.parameter);
82
+ params.set('script.param', this.scriptData.after.parameter);
83
83
  if (this.scriptData.presort)
84
- params.set("script.presort", this.scriptData.presort.name);
84
+ params.set('script.presort', this.scriptData.presort.name);
85
85
  if (this.scriptData.presort?.parameter)
86
- params.set("script.presort.param", this.scriptData.presort.parameter);
86
+ params.set('script.presort.param', this.scriptData.presort.parameter);
87
87
  if (this.scriptData.prerequest)
88
- params.set("script.prerequest", this.scriptData.prerequest.name);
88
+ params.set('script.prerequest', this.scriptData.prerequest.name);
89
89
  if (this.scriptData.prerequest?.parameter)
90
- params.set("script.prerequest.param", this.scriptData.prerequest.parameter);
91
- let portals = Object.keys(this.portals);
92
- for (let portal of portals) {
93
- params.set(`_offset.${portal.toString()}`, (this.portals[portal]?.limit || "").toString());
94
- params.set(`_offset.${portal.toString()}`, (this.portals[portal]?.offset || "").toString());
90
+ params.set('script.prerequest.param', this.scriptData.prerequest.parameter);
91
+ const portals = Object.keys(this.portals);
92
+ for (const portal of portals) {
93
+ params.set(`_offset.${portal.toString()}`, (this.portals[portal]?.limit ?? '').toString());
94
+ params.set(`_offset.${portal.toString()}`, (this.portals[portal]?.offset ?? '').toString());
95
95
  }
96
96
  params.set('portal', JSON.stringify(portals));
97
97
  return params;
98
98
  }
99
+ /**
100
+ * Configures any FileMaker scripts to be run as a part of the request
101
+ *
102
+ * @param {ScriptRequestData} scripts - The script request data to set.
103
+ * @return {this} - The current instance of the class.
104
+ */
99
105
  scripts(scripts) {
100
106
  this.scriptData = scripts;
101
107
  return this;
102
108
  }
109
+ /**
110
+ * Sorts the data based on the given field name and sort order.
111
+ *
112
+ * @param {string} fieldName - The name of the field by which the data should be sorted.
113
+ * @param {SortOrder} sortOrder - The sort order to be applied (either "asc" for ascending or "desc" for descending).
114
+ *
115
+ * @return {this} - Returns the current instance of the object.
116
+ */
103
117
  sort(fieldName, sortOrder) {
104
118
  this.sortData.push({ fieldName, sortOrder });
105
119
  return this;
106
120
  }
107
121
  parseFindRequest(query) {
108
- let out = {};
109
- for (let key of Object.keys(query)) {
122
+ const out = {};
123
+ for (const key of Object.keys(query)) {
110
124
  out[key] = query[key][FindRequestSymbol].map(item => {
111
- if (typeof item === "string")
125
+ if (typeof item === 'string')
112
126
  return item;
113
127
  // Re-write date into correct format
114
128
  return item
115
129
  .moment
116
130
  .clone()
117
131
  .utcOffset(this.layout.database.host.timezoneOffsetFunc(item.moment))
118
- .format(item.type === "date" ?
119
- this.layout.database.host.dateFormat :
120
- item.type == "time" ?
121
- this.layout.database.host.timeFormat :
122
- this.layout.database.host.timeStampFormat);
123
- }).join("");
132
+ .format(item.type === 'date'
133
+ ? this.layout.database.host.dateFormat
134
+ : item.type == 'time'
135
+ ? this.layout.database.host.timeFormat
136
+ : this.layout.database.host.timeStampFormat);
137
+ }).join('');
138
+ console.log(this.layout.database.host.timeStampFormat, out[key]);
124
139
  }
125
140
  return out;
126
141
  }
142
+ /**
143
+ * Adds a new request/query to the list of queries.
144
+ *
145
+ * @param {FindRequest} query - The find request to be added.
146
+ * @param {boolean} [omit=false] - Flag to indicate if the find request should be omitted.
147
+ * @return {Object} - The current object instance.
148
+ */
127
149
  addRequest(query, omit = false) {
128
150
  this.queries.push({ req: this.parseFindRequest(query), omit });
129
151
  return this;
130
152
  }
131
- fetch() {
132
- return this.performFind(this.offset, this.limit);
153
+ /**
154
+ * Perform a fetch operation.
155
+ *
156
+ * @returns {Promise} A promise that resolves with the result of the fetch operation.
157
+ */
158
+ async fetch() {
159
+ return await this.performFind(this.offset, this.limit);
133
160
  }
134
161
  async performFind(offset, limit) {
135
- let trace = new Error();
162
+ const trace = new Error();
136
163
  await this.layout.getLayoutMeta();
137
- const is_find = this.isFindRequest;
138
- let endpoint = this.layout.endpoint + (is_find ? "/_find" : "/records");
139
- if (!is_find)
140
- endpoint += "?" + new URLSearchParams(this.generateParamsURL(offset, limit)).toString();
164
+ const isFind = this.isFindRequest;
165
+ let endpoint = this.layout.endpoint + (isFind ? '/_find' : '/records');
166
+ if (!isFind)
167
+ endpoint += '?' + new URLSearchParams(this.generateParamsURL(offset, limit)).toString();
141
168
  const reqData = {
142
169
  // port: 443,
143
- method: is_find ? "POST" : "GET",
144
- body: is_find ? JSON.stringify(this.generateParamsBody(offset, limit)) : undefined,
170
+ method: isFind ? 'POST' : 'GET',
171
+ body: isFind ? JSON.stringify(this.generateParamsBody(offset, limit)) : undefined
145
172
  };
146
173
  try {
147
- let res = await this.layout.database.apiRequestJSON(endpoint, reqData);
148
- if (res.messages[0].code === "0" && res.response) {
174
+ const res = await this.layout.database._apiRequestJSON(endpoint, reqData);
175
+ if (res.messages[0].code === '0' && res.response) {
149
176
  // console.log("RESOLVING")
150
177
  if (!this.layout.metadata)
151
178
  await this.layout.getLayoutMeta();
@@ -169,28 +196,28 @@ export class RecordGetOperation {
169
196
  }
170
197
  [Symbol.asyncIterator]() {
171
198
  let nextOffset = this.offset;
172
- let startOffset = JSON.parse(JSON.stringify(this.offset));
173
- let limit = this.limit;
174
- let exit_after_last_record = false;
199
+ const startOffset = JSON.parse(JSON.stringify(this.offset));
200
+ const limit = this.limit;
201
+ let exitAfterLastRecord = false;
175
202
  let records = [];
176
203
  const fetch = async () => {
177
- const theoretical_limit = (limit - nextOffset) + startOffset;
178
- if (theoretical_limit === 0) {
179
- exit_after_last_record = true;
204
+ const theoreticalLimit = (limit - nextOffset) + startOffset;
205
+ if (theoreticalLimit === 0) {
206
+ exitAfterLastRecord = true;
180
207
  records = [];
181
208
  return;
182
209
  }
183
- records = await this.performFind(nextOffset, theoretical_limit < 100 ? theoretical_limit : 100);
210
+ records = await this.performFind(nextOffset, theoreticalLimit < 100 ? theoreticalLimit : 100);
184
211
  nextOffset += 100;
185
212
  if (records.length < 100)
186
- exit_after_last_record = true;
213
+ exitAfterLastRecord = true;
187
214
  };
188
215
  return {
189
216
  next: async () => {
190
- if (records.length === 0 && !exit_after_last_record) {
217
+ if (records.length === 0 && !exitAfterLastRecord) {
191
218
  await fetch();
192
219
  }
193
- if (records.length === 0 && exit_after_last_record) {
220
+ if (records.length === 0 && exitAfterLastRecord) {
194
221
  return { done: true, value: undefined };
195
222
  }
196
223
  else {