@jd-data-limited/easy-fm 4.1.6 → 4.1.8

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