@jd-data-limited/easy-fm 4.1.5 → 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.
- package/dist/bin/cli.js +17 -0
- package/dist/bin/generateTypes.js +161 -0
- package/{src → dist}/connection/FMHost.js +3 -2
- package/{src → dist}/connection/database.d.ts +0 -1
- package/{src → dist}/connection/database.js +22 -39
- package/dist/index.d.ts +18 -0
- package/dist/index.js +16 -0
- package/{src → dist}/records/field.d.ts +0 -1
- package/{src → dist}/records/getOperations/recordGetOperation.d.ts +6 -2
- package/{src → dist}/records/getOperations/recordGetOperation.js +9 -5
- package/{src → dist}/records/layoutRecord.d.ts +1 -1
- package/{src → dist}/records/layoutRecord.js +9 -7
- package/{src → dist}/records/recordBase.d.ts +0 -1
- package/{src → dist}/types.d.ts +12 -2
- package/package.json +23 -12
- package/src/FMError.ts +0 -1033
- package/src/bin/cli.js +0 -17
- package/src/bin/cli.ts +0 -22
- package/src/bin/generateTypes.d.ts +0 -1
- package/src/bin/generateTypes.js +0 -161
- package/src/bin/generateTypes.ts +0 -189
- package/src/connection/FMHost.ts +0 -123
- package/src/connection/HostBase.ts +0 -18
- package/src/connection/database.ts +0 -257
- package/src/connection/databaseBase.ts +0 -19
- package/src/connection/generateAuthorizationHeaders.ts +0 -32
- package/src/connection/requestHoldable.d.ts +0 -10
- package/src/connection/requestHoldable.js +0 -44
- package/src/databaseStructure.ts +0 -10
- package/src/index.d.ts +0 -18
- package/src/index.js +0 -18
- package/src/index.ts +0 -47
- package/src/layouts/layout.ts +0 -66
- package/src/layouts/layoutBase.ts +0 -16
- package/src/layouts/layoutInterface.ts +0 -13
- package/src/layouts/layoutRecordManager.ts +0 -66
- package/src/layouts/recordFieldsMap.ts +0 -7
- package/src/models/apiResults.ts +0 -110
- package/src/records/field.ts +0 -235
- package/src/records/getOperations/recordGetOperation.ts +0 -268
- package/src/records/layoutRecord.ts +0 -246
- package/src/records/layoutRecordBase.ts +0 -10
- package/src/records/portal.ts +0 -43
- package/src/records/portalBase.ts +0 -14
- package/src/records/portalRecord.ts +0 -52
- package/src/records/recordBase.ts +0 -91
- package/src/types.ts +0 -150
- package/src/utils/query.ts +0 -59
- /package/{src → dist}/FMError.d.ts +0 -0
- /package/{src → dist}/FMError.js +0 -0
- /package/{src → dist}/bin/cli.d.ts +0 -0
- /package/{src/bin/dist → dist}/bin/generateTypes.d.ts +0 -0
- /package/{src → dist}/connection/FMHost.d.ts +0 -0
- /package/{src → dist}/connection/HostBase.d.ts +0 -0
- /package/{src → dist}/connection/HostBase.js +0 -0
- /package/{src → dist}/connection/databaseBase.d.ts +0 -0
- /package/{src → dist}/connection/databaseBase.js +0 -0
- /package/{src → dist}/connection/generateAuthorizationHeaders.d.ts +0 -0
- /package/{src → dist}/connection/generateAuthorizationHeaders.js +0 -0
- /package/{src → dist}/databaseStructure.d.ts +0 -0
- /package/{src → dist}/databaseStructure.js +0 -0
- /package/{src → dist}/layouts/layout.d.ts +0 -0
- /package/{src → dist}/layouts/layout.js +0 -0
- /package/{src → dist}/layouts/layoutBase.d.ts +0 -0
- /package/{src → dist}/layouts/layoutBase.js +0 -0
- /package/{src → dist}/layouts/layoutInterface.d.ts +0 -0
- /package/{src → dist}/layouts/layoutInterface.js +0 -0
- /package/{src → dist}/layouts/layoutRecordManager.d.ts +0 -0
- /package/{src → dist}/layouts/layoutRecordManager.js +0 -0
- /package/{src → dist}/layouts/recordFieldsMap.d.ts +0 -0
- /package/{src → dist}/layouts/recordFieldsMap.js +0 -0
- /package/{src → dist}/models/apiResults.d.ts +0 -0
- /package/{src → dist}/models/apiResults.js +0 -0
- /package/{src → dist}/records/field.js +0 -0
- /package/{src → dist}/records/layoutRecordBase.d.ts +0 -0
- /package/{src → dist}/records/layoutRecordBase.js +0 -0
- /package/{src → dist}/records/portal.d.ts +0 -0
- /package/{src → dist}/records/portal.js +0 -0
- /package/{src → dist}/records/portalBase.d.ts +0 -0
- /package/{src → dist}/records/portalBase.js +0 -0
- /package/{src → dist}/records/portalRecord.d.ts +0 -0
- /package/{src → dist}/records/portalRecord.js +0 -0
- /package/{src → dist}/records/recordBase.js +0 -0
- /package/{src → dist}/types.js +0 -0
- /package/{src → dist}/utils/query.d.ts +0 -0
- /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
|
-
}
|
package/src/databaseStructure.ts
DELETED
|
@@ -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
|
-
}
|
package/src/layouts/layout.ts
DELETED
|
@@ -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
|
-
}
|