@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,257 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {EventEmitter} from 'events'
6
- import {generateAuthorizationHeaders} from './generateAuthorizationHeaders.js'
7
- import {FMError} from '../FMError.js'
8
- import {type LayoutInterface} from '../layouts/layoutInterface.js'
9
- import {Layout} from '../layouts/layout.js'
10
- import {type databaseOptionsBase, type databaseOptionsWithExternalSources, type Script} from '../types.js'
11
- import {type HostBase} from './HostBase.js'
12
- import {type DatabaseBase} from './databaseBase.js'
13
- import {type ApiLayout, type ApiResults} from '../models/apiResults.js'
14
- import {type DatabaseStructure} from '../databaseStructure.js'
15
- import fetch, {type HeadersInit, type RequestInfo, type RequestInit, type Response} from 'node-fetch'
16
- // @ts-expect-error - fetchWithCookies does not have available typescript types
17
- import fetchWithCookies, {CookieJar} from 'node-fetch-cookies'
18
-
19
- /**
20
- * Represents a database connection.
21
- * @template T - The structure of the database.
22
- */
23
- export class Database<T extends DatabaseStructure> extends EventEmitter implements DatabaseBase {
24
- private _token: string = ''
25
- readonly host: HostBase
26
- private readonly connection_details: databaseOptionsWithExternalSources
27
- private cookies = new CookieJar()
28
- readonly name: string
29
- readonly debug: boolean
30
- readonly #layoutCache = new Map<string, Layout<any>>()
31
-
32
- constructor (host: HostBase, conn: databaseOptionsWithExternalSources) {
33
- super()
34
- this.host = host
35
- this.name = conn.database
36
- this.connection_details = conn
37
- this.debug = conn.debug ?? false
38
- }
39
-
40
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
41
- private generateExternalSourceLogin (data: databaseOptionsBase) {
42
- if (data.credentials.method === 'filemaker') {
43
- const _data = data.credentials
44
- return {
45
- database: data.database,
46
- username: _data.username,
47
- password: _data.password
48
- }
49
- }
50
- else {
51
- throw new Error('Not yet supported login method')
52
- }
53
- }
54
-
55
- /**
56
- * Logs out the user by deleting the current session token.
57
- * Throws an error if the user is not logged in.
58
- *
59
- * @returns {Promise<void>} A promise that resolves with no value once the logout is successful.
60
- * @throws {Error} Throws an error if the user is not logged in.
61
- */
62
- async logout (): Promise<void> {
63
- if (this.token === '') throw new Error('Not logged in')
64
-
65
- const _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
66
- method: 'DELETE',
67
- headers: {
68
- 'content-type': 'application/json'
69
- }
70
- })
71
- await _fetch.json()
72
- this._token = ''
73
- }
74
-
75
- /**
76
- * Logs in to the database. Not required, as this is often done automatically
77
- *
78
- * @param {boolean} [forceLogin=false] - Whether to force login even if already logged in.
79
- * @throws {Error} - Throws an error if already logged in and forceLogin is false.
80
- * @throws {FMError} - Throws an FMError if login fails.
81
- * @return {Promise<string>} - Returns a promise that resolves to the access token upon successful login.
82
- */
83
- async login (forceLogin = false) {
84
- if (this.token !== '' && !forceLogin) return
85
-
86
- // Reset cookies
87
- this.cookies = new CookieJar()
88
-
89
- await this.host.getMetadata()
90
-
91
- if (this.connection_details.credentials.method === 'token') {
92
- this._token = (this.connection_details.credentials).token
93
- return this.token
94
- }
95
- else {
96
- const url = new URL(`${this.endpoint}/sessions`)
97
- url.hostname = this.host.hostname
98
- const res = await fetch(url, {
99
- method: 'POST',
100
- headers: generateAuthorizationHeaders(this.connection_details.credentials) as unknown as HeadersInit,
101
- body: JSON.stringify({
102
- fmDataSource: this.connection_details.externalSources.map(i => {
103
- const _i = i
104
- return this.generateExternalSourceLogin(_i)
105
- })
106
- })
107
- })
108
- const _res = (await res.json()) as any
109
- if (res.status === 200) {
110
- this._token = res.headers.get('x-fm-data-access-token') ?? ''
111
- return this._token
112
- }
113
- else {
114
- throw new FMError(
115
- _res.messages[0].code as string | number,
116
- _res.status as number,
117
- res
118
- )
119
- }
120
- }
121
- }
122
-
123
- get token () {
124
- return this._token
125
- }
126
-
127
- /**
128
- * Returns the endpoint URL for the database connection.
129
- *
130
- * @returns {string} The endpoint URL.
131
- */
132
- get endpoint (): string {
133
- return `${this.host.protocol}//${this.host.hostname}/fmi/data/v2/databases/${this.name}`
134
- }
135
-
136
- async _apiRequestRaw (url: URL | RequestInfo, options: RequestInit & {
137
- headers?: Record<string, string>
138
- useCookieJar?: boolean
139
- retries?: number
140
- } = {}, autoRelogin = true): Promise<Response> {
141
- if (this.debug) {
142
- console.log(
143
- `EASYFM DEBUG: ${JSON.stringify(options)} ${
144
- url instanceof URL
145
- ? url.toString()
146
- : typeof url === 'string' ? url : url.url
147
- }`
148
- )
149
- }
150
- const reqIsToDBHost = (
151
- url instanceof URL
152
- ? url
153
- : typeof url === 'string'
154
- ? new URL(url)
155
- : new URL(url.url)
156
- ).hostname === this.host.hostname
157
- if (reqIsToDBHost && this.token === '') await this.login(true)
158
-
159
- if (!options.headers) options.headers = {}
160
- if (reqIsToDBHost) options.headers.authorization = 'Bearer ' + this._token
161
- // options.redirect = "manual"
162
- // options.headers.cookies = this._generateCookieHeader()
163
- // console.log(options.headers.cookies)
164
- // options.rejectUnauthorized = this.host.verify
165
-
166
- const _fetch: Response =
167
- options.useCookieJar
168
- ? await fetchWithCookies(this.cookies, url, options)
169
- : await fetch(url, options)
170
- // if (_fetch.headers.get('set-cookie')) {
171
- // console.log(_fetch.headers.get('set-cookie'))
172
- // for (const cookie of (_fetch.headers.get("Set-Cookie") ?? "").split("; ")) {
173
- // const cookieSplit = cookie.split('=')
174
- // console.log("COOKIE SPLIT:", cookieSplit)
175
- // if (cookieSplit[1]) this.cookies[cookieSplit[0]] = cookieSplit[1]
176
- // }
177
- // }
178
- // console.log(this.cookies, _fetch.status)
179
- // // console.log(this, this.cookies, url)
180
- // console.trace()
181
- if (!_fetch.ok && (!options.retries || options.retries > 0)) {
182
- if (this.debug) {
183
- console.log(`EASYFM DEBUG: RE-ATTEMPTING REQUEST (${_fetch.status}) ${
184
- url instanceof URL
185
- ? url.toString()
186
- : typeof url === 'string' ? url : url.url
187
- }`)
188
- }
189
- return await this._apiRequestRaw(url, {...options, retries: (options?.retries ?? 1) - 1})
190
- }
191
- else if (_fetch.status === 401 && reqIsToDBHost && autoRelogin) {
192
- await this.login(true)
193
- return await this._apiRequestRaw(url, options, false)
194
- }
195
- else return _fetch
196
- }
197
-
198
- async _apiRequestJSON<T = any>(url: URL | RequestInfo, options: RequestInit & {
199
- headers?: Record<string, string>
200
- } = {}): Promise<ApiResults<T>> {
201
- if (!options.headers) options.headers = {}
202
- options.headers['content-type'] = options.headers['content-type'] ? options.headers['content-type'] : 'application/json'
203
- const _fetch = await this._apiRequestRaw(url, options)
204
- const data = await _fetch.json() as ApiResults<T>
205
-
206
- // Remove response if it is empty. This makes checking for an empty response easier
207
- if (data.response && Object.keys(data.response).length === 0) delete data.response
208
-
209
- // console.log(data.messages[0])
210
- if (data.messages[0].code !== '0') {
211
- throw new FMError(data.messages[0].code, _fetch.status, data)
212
- }
213
-
214
- data.httpStatus = _fetch.status
215
- return data
216
- }
217
-
218
- /**
219
- * Retrieves a list of layouts in the current FileMaker database.
220
- *
221
- * @returns {Promise<Layout[]>} A promise that resolves to an array of Layout objects.
222
- * @throws {FMError} If there was an error retrieving the layouts.
223
- */
224
- async listLayouts (page: number = 0) {
225
- const req = await this._apiRequestJSON<{ layouts: ApiLayout[] }>(`${this.endpoint}/layouts?page=${encodeURIComponent(page)}`)
226
- console.log(req)
227
- if (!req.response) throw new FMError(req.messages[0].code, req.httpStatus, req.messages[0].message)
228
-
229
- const cycleLayoutNames = (layouts: ApiLayout[]) => {
230
- let names: string[] = []
231
- for (const layout of layouts) {
232
- if (layout.folderLayoutNames) names = names.concat(cycleLayoutNames(layout.folderLayoutNames))
233
- else names.push(layout.name)
234
- }
235
- return names
236
- }
237
- return cycleLayoutNames(req.response.layouts).map(layout => new Layout(this, layout))
238
- }
239
-
240
- layout<R extends keyof T['layouts']>(name: R): Layout<T['layouts'][R]>
241
- layout<R extends LayoutInterface>(name: string): Layout<R>
242
- layout (name: string): Layout<any> {
243
- let layout = this.#layoutCache.get(name)
244
- if (layout) return layout
245
- layout = new Layout<LayoutInterface>(this, name)
246
- this.#layoutCache.set(name, layout)
247
- return layout
248
- }
249
-
250
- clearLayoutCache () {
251
- this.#layoutCache.clear()
252
- }
253
-
254
- script (name: string, parameter = ''): Script {
255
- return ({name, parameter} satisfies Script)
256
- }
257
- }
@@ -1,19 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {type HostBase} from './HostBase.js'
6
- import {type ApiResults} from '../models/apiResults.js'
7
- import {type RequestInfo, type RequestInit, type Response} from 'node-fetch'
8
-
9
- export interface DatabaseBase {
10
- host: HostBase
11
- readonly name: string
12
- endpoint: string
13
- token: string
14
-
15
- // layouts: DatabaseStructure["layouts"]
16
-
17
- _apiRequestJSON: <T = unknown>(url: URL | RequestInfo, options?: RequestInit & { headers?: Record<string, string> } | undefined, autoRelogin?: boolean) => Promise<ApiResults<T>>
18
- _apiRequestRaw: (url: URL | RequestInfo, options?: RequestInit & { headers?: Record<string, string>, useCookieJar?: boolean } | undefined) => Promise<Response>
19
- }
@@ -1,32 +0,0 @@
1
- /*
2
- * Copyright (c) 2023. See LICENSE file for more information
3
- */
4
-
5
- import {
6
- type AuthorizationHeaders,
7
- type AuthorizationHeadersOAuth,
8
- type loginOptionsClaris,
9
- type loginOptionsFileMaker,
10
- type loginOptionsOAuth
11
- } from '../types.js'
12
-
13
- export function generateAuthorizationHeaders (credentials: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris): AuthorizationHeaders | AuthorizationHeadersOAuth {
14
- switch (credentials.method) {
15
- case 'filemaker':
16
- return {
17
- 'Content-Type': 'application/json',
18
- Authorization: 'Basic ' + Buffer.from((credentials).username + ':' + (credentials).password).toString('base64')
19
- } satisfies AuthorizationHeaders
20
- case 'claris':
21
- return {
22
- 'Content-Type': 'application/json',
23
- Authorization: (credentials).claris.fmid
24
- } satisfies AuthorizationHeaders
25
- case 'oauth':
26
- return {
27
- 'Content-Type': 'application/json',
28
- 'X-FM-Data-OAuth-RequestId': (credentials).oauth.requestId,
29
- 'X-FM-Data-OAuth-Identifier': (credentials).oauth.requestIdentifier
30
- } satisfies AuthorizationHeadersOAuth
31
- }
32
- }
@@ -1,10 +0,0 @@
1
- import { type RequestInfo, type RequestInit, Response } from 'node-fetch';
2
- export declare class RequestHoldableClass {
3
- #private;
4
- protected cookies: any;
5
- _addNewFuncToQueue(func: () => Promise<void>): void;
6
- protected _holdableFetch(url: URL | RequestInfo, options: RequestInit & {
7
- headers?: Record<string, string>;
8
- }): Promise<Response>;
9
- }
10
- export declare function RequestHoldable<PROPS extends any[], R = any>(originalMethod: (...p: PROPS) => Promise<R>, context: ClassMethodDecoratorContext<RequestHoldableClass>): (this: RequestHoldableClass, ...args: PROPS) => Promise<R>;
@@ -1,44 +0,0 @@
1
- /*
2
- * Copyright (c) 2024. See LICENSE file for more information
3
- */
4
- // @ts-expect-error - NO types module for this
5
- import fetchWithCookies, { CookieJar } from 'node-fetch-cookies';
6
- export class RequestHoldableClass {
7
- #functionQueue = [];
8
- #requestsAreBeingHeld = false;
9
- cookies = new CookieJar();
10
- _addNewFuncToQueue(func) {
11
- this.#functionQueue.push(func);
12
- if (this.#functionQueue.length === 1)
13
- void this.#executeQueue();
14
- }
15
- async _holdableFetch(url, options) {
16
- url.
17
- if(!this.#requestsAreBeingHeld);
18
- {
19
- // eslint-disable-next-line @typescript-eslint/return-await
20
- return await fetchWithCookies(this.cookies, url, options);
21
- }
22
- }
23
- async #executeQueue() {
24
- while (this.#functionQueue.length !== 0) {
25
- try {
26
- await this.#functionQueue[0]();
27
- }
28
- catch (e) {
29
- console.error(e);
30
- }
31
- this.#functionQueue.shift();
32
- }
33
- }
34
- }
35
- export function RequestHoldable(originalMethod, context) {
36
- // eslint-disable-next-line @typescript-eslint/promise-function-async
37
- function replacementMethod(...args) {
38
- return new Promise((resolve, reject) => {
39
- // eslint-disable-next-line @typescript-eslint/promise-function-async
40
- this._addNewFuncToQueue(() => originalMethod.call(this, ...args).then(resolve).catch(reject));
41
- });
42
- }
43
- return replacementMethod;
44
- }
@@ -1,10 +0,0 @@
1
- /*
2
- * Copyright (c) 2024. See LICENSE file for more information
3
- */
4
-
5
- // import * as btoa from "btoa";
6
- import {type LayoutInterface} from './layouts/layoutInterface.js'
7
-
8
- export interface DatabaseStructure {
9
- layouts: Record<string, LayoutInterface>
10
- }
package/src/index.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import FMHost from './connection/FMHost.js';
2
- import { FMError } from './FMError.js';
3
- import * as TYPES from './types.js';
4
- import { type PickPortals } from './types.js';
5
- import { Database } from './connection/database.js';
6
- import { Layout } from './layouts/layout.js';
7
- import { RecordBase } from './records/recordBase.js';
8
- import { LayoutRecord } from './records/layoutRecord.js';
9
- import { type LayoutInterface, type PortalInterface } from './layouts/layoutInterface.js';
10
- import { PortalRecord } from './records/portalRecord.js';
11
- import { Portal } from './records/portal.js';
12
- import { type FindRequest, type FindRequestRaw, RecordGetOperation } from './records/getOperations/recordGetOperation.js';
13
- import { LayoutRecordManager } from './layouts/layoutRecordManager.js';
14
- import { type RecordFieldsMap } from './layouts/recordFieldsMap.js';
15
- import { asDate, asTime, asTimestamp, query, queryEscape } from './utils/query.js';
16
- import { type Container, Field } from './records/field.js';
17
- export default FMHost;
18
- export { FMError, TYPES, Database, Layout, RecordBase, LayoutRecord, type LayoutInterface, type RecordFieldsMap, type PortalInterface, PortalRecord, Portal, Field, RecordGetOperation, LayoutRecordManager, type Container, type PickPortals, type FindRequestRaw, type FindRequest, query, queryEscape, asDate, asTime, asTimestamp };
package/src/index.js DELETED
@@ -1,18 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
- import FMHost from './connection/FMHost.js';
5
- import { FMError } from './FMError.js';
6
- import * as TYPES from './types.js';
7
- import { Database } from './connection/database.js';
8
- import { Layout } from './layouts/layout.js';
9
- import { RecordBase } from './records/recordBase.js';
10
- import { LayoutRecord } from './records/layoutRecord.js';
11
- import { PortalRecord } from './records/portalRecord.js';
12
- import { Portal } from './records/portal.js';
13
- import { RecordGetOperation } from './records/getOperations/recordGetOperation.js';
14
- import { LayoutRecordManager } from './layouts/layoutRecordManager.js';
15
- import { asDate, asTime, asTimestamp, query, queryEscape } from './utils/query.js';
16
- import { Field } from './records/field.js';
17
- export default FMHost;
18
- export { FMError, TYPES, Database, Layout, RecordBase, LayoutRecord, PortalRecord, Portal, Field, RecordGetOperation, LayoutRecordManager, query, queryEscape, asDate, asTime, asTimestamp };
package/src/index.ts DELETED
@@ -1,47 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import FMHost from './connection/FMHost.js'
6
- import {FMError} from './FMError.js'
7
- import * as TYPES from './types.js'
8
- import {type PickPortals} from './types.js'
9
- import {Database} from './connection/database.js'
10
- import {Layout} from './layouts/layout.js'
11
- import {RecordBase} from './records/recordBase.js'
12
- import {LayoutRecord} from './records/layoutRecord.js'
13
- import {type LayoutInterface, type PortalInterface} from './layouts/layoutInterface.js'
14
- import {PortalRecord} from './records/portalRecord.js'
15
- import {Portal} from './records/portal.js'
16
- import {type FindRequest, type FindRequestRaw, RecordGetOperation} from './records/getOperations/recordGetOperation.js'
17
- import {LayoutRecordManager} from './layouts/layoutRecordManager.js'
18
- import {type RecordFieldsMap} from './layouts/recordFieldsMap.js'
19
- import {asDate, asTime, asTimestamp, query, queryEscape} from './utils/query.js'
20
- import {type Container, Field} from './records/field.js'
21
-
22
- export default FMHost
23
- export {
24
- FMError,
25
- TYPES,
26
- Database,
27
- Layout,
28
- RecordBase,
29
- LayoutRecord,
30
- type LayoutInterface,
31
- type RecordFieldsMap,
32
- type PortalInterface,
33
- PortalRecord,
34
- Portal,
35
- Field,
36
- RecordGetOperation,
37
- LayoutRecordManager,
38
- type Container,
39
- type PickPortals,
40
- type FindRequestRaw,
41
- type FindRequest,
42
- query,
43
- queryEscape,
44
- asDate,
45
- asTime,
46
- asTimestamp
47
- }
@@ -1,66 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {LayoutRecordManager} from './layoutRecordManager.js'
6
- import {type Script, type ScriptResult} from '../types.js'
7
- import {type LayoutInterface} from './layoutInterface.js'
8
- import {FMError} from '../FMError.js'
9
- import {type LayoutBase} from './layoutBase.js'
10
- import {type DatabaseBase} from '../connection/databaseBase.js'
11
- import {type ApiLayoutMetadata, type ApiScriptResult} from '../models/apiResults.js'
12
-
13
- export class Layout<T extends LayoutInterface> implements LayoutBase {
14
- readonly database: DatabaseBase
15
- readonly name: string
16
- readonly records = new LayoutRecordManager<T>(this)
17
- metadata: ApiLayoutMetadata | null = null
18
-
19
- constructor (database: DatabaseBase, name: string) {
20
- this.database = database
21
- this.name = name
22
- }
23
-
24
- get endpoint () {
25
- return `${this.database.endpoint}/layouts/${this.name}`
26
- }
27
-
28
- /**
29
- * Executes a FileMaker script on this layout asynchronously and returns the result.
30
- * @param {Script} script - The script to be executed.
31
- * @returns {Promise<ScriptResult>} - A promise that resolves to the script result or rejects with an error.
32
- */
33
- async runScript (script: Script): Promise<ScriptResult> {
34
- let url = `${this.endpoint}/script/${encodeURIComponent(script.name)}`
35
- if (script.parameter) url += '?script.param=' + encodeURIComponent(script.parameter)
36
- const res = await this.database._apiRequestJSON<ApiScriptResult>(url, {
37
- method: 'GET'
38
- })
39
- if (res.response && res.messages[0].code === '0') {
40
- const error = parseInt(res.response.scriptError)
41
- return {
42
- scriptError: error ? new FMError(error, 200, res) : undefined,
43
- scriptResult: res.response.scriptResult
44
- }
45
- } else {
46
- throw new FMError(res.messages[0].code, res.httpStatus, res)
47
- }
48
- }
49
-
50
- /**
51
- * Retrieves the layout metadata
52
- *
53
- * @returns {Promise<ApiLayoutMetadata>} The layout metadata.
54
- * @throws {FMError} If an error occurs during the API request.
55
- */
56
- public async getLayoutMeta (): Promise<ApiLayoutMetadata> {
57
- if (this.metadata) {
58
- return this.metadata
59
- }
60
-
61
- const res = await this.database._apiRequestJSON<ApiLayoutMetadata>(this.endpoint)
62
- if (!res.response) throw new FMError(res.messages[0].code, res.httpStatus, res)
63
- this.metadata = res.response
64
- return this.metadata
65
- }
66
- }
@@ -1,16 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {type Script, type ScriptResult} from '../types.js'
6
- import {type DatabaseBase} from '../connection/databaseBase.js'
7
- import {type ApiLayoutMetadata} from '../models/apiResults.js'
8
-
9
- export interface LayoutBase {
10
- readonly name: string
11
- metadata: ApiLayoutMetadata | null
12
- endpoint: string
13
- runScript: (script: Script) => Promise<ScriptResult>
14
- getLayoutMeta: () => Promise<ApiLayoutMetadata>
15
- database: DatabaseBase
16
- }
@@ -1,13 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {type Portal} from '../records/portal.js'
6
- import {type RecordFieldsMap} from './recordFieldsMap.js'
7
-
8
- export interface LayoutInterface {
9
- fields: RecordFieldsMap
10
- portals: PortalInterface
11
- }
12
-
13
- export type PortalInterface = Record<string | number | symbol, Portal<RecordFieldsMap>>
@@ -1,66 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {LayoutRecord} from '../records/layoutRecord.js'
6
- import {type LayoutInterface} from './layoutInterface.js'
7
- import {type LayoutBase} from './layoutBase.js'
8
- import {type GetOperationOptions, RecordGetOperation} from '../records/getOperations/recordGetOperation.js'
9
- import {type PickPortals, type RecordFetchOptions} from '../types.js'
10
- import {type ApiFieldData} from '../models/apiResults.js'
11
-
12
- /**
13
- * Manager class for handling layout records.
14
- */
15
- export class LayoutRecordManager<T extends LayoutInterface> {
16
- readonly layout: LayoutBase
17
- constructor (layout: LayoutBase) {
18
- this.layout = layout
19
- }
20
-
21
- /**
22
- * Creates a new layout record with the provided options.
23
- *
24
- * @param {OPTIONS} options - The options for creating the layout record.
25
- * @return {Promise<LayoutRecord<PickPortals<T, OPTIONS['portals'][number]>>>}
26
- * The newly created layout record.
27
- */
28
- async create<OPTIONS extends RecordFetchOptions>(options: OPTIONS): Promise<LayoutRecord<
29
- PickPortals<T, OPTIONS['portals'][number]>
30
- >> {
31
- const metadata = await this.layout.getLayoutMeta()
32
- const fields: ApiFieldData = {}
33
- for (const _field of metadata.fieldMetaData) {
34
- fields[_field.name] = ''
35
- }
36
- const portals: Record<string, []> = {}
37
- for (const _portal of Object.keys(metadata.portalMetaData)) portals[_portal] = []
38
- return new LayoutRecord(this.layout, -1, 0, fields, portals)
39
- }
40
-
41
- /**
42
- * Retrieves a layout record based on the given recordId.
43
- *
44
- * @param {number} recordId - The identifier of the record to retrieve.
45
- *
46
- * @returns {Promise<LayoutRecord<PickPortals<T, never>>>} - A Promise that resolves with the retrieved layout record.
47
- */
48
- async get (recordId: number): Promise<LayoutRecord<
49
- PickPortals<T, never>
50
- >> {
51
- await this.layout.getLayoutMeta()
52
- const record = new LayoutRecord<PickPortals<T, never>>(this.layout, recordId)
53
- await record.get()
54
- return record
55
- }
56
-
57
- /**
58
- * Creates a new instance of RecordGetOperation with the given options.
59
- *
60
- * @param {Array} options - An array of options for the operation.
61
- * @return {RecordGetOperation} - A new instance of RecordGetOperation.
62
- */
63
- list<OPTIONS extends GetOperationOptions<T>>(options: OPTIONS) {
64
- return new RecordGetOperation<T, OPTIONS>(this.layout, options)
65
- }
66
- }
@@ -1,7 +0,0 @@
1
- /*
2
- * Copyright (c) 2023-2024. See LICENSE file for more information
3
- */
4
-
5
- import {type Field, type FieldValue} from '../records/field.js'
6
-
7
- export type RecordFieldsMap = Record<string, Field<FieldValue>>