@jd-data-limited/easy-fm 4.1.1 → 4.1.3
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/package.json +1 -1
- package/src/connection/database.d.ts +1 -1
- package/src/connection/database.js +8 -13
- package/src/connection/database.ts +26 -35
- package/src/connection/databaseBase.d.ts +1 -1
- package/src/connection/databaseBase.ts +1 -1
- package/src/records/field.d.ts +8 -2
- package/src/records/field.js +12 -18
- package/src/records/field.ts +33 -43
- package/src/records/getOperations/recordGetOperation.js +0 -1
- package/src/records/getOperations/recordGetOperation.ts +0 -1
- package/src/records/layoutRecord.js +0 -1
- package/src/records/layoutRecord.ts +0 -1
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import { type HostBase } from './HostBase.js';
|
|
|
7
7
|
import { type DatabaseBase } from './databaseBase.js';
|
|
8
8
|
import { type ApiResults } from '../models/apiResults.js';
|
|
9
9
|
import { type DatabaseStructure } from '../databaseStructure.js';
|
|
10
|
-
import { RequestInfo, RequestInit, Response } from 'node-fetch';
|
|
10
|
+
import { type RequestInfo, type RequestInit, type Response } from 'node-fetch';
|
|
11
11
|
/**
|
|
12
12
|
* Represents a database connection.
|
|
13
13
|
* @template T - The structure of the database.
|
|
@@ -6,8 +6,8 @@ import { generateAuthorizationHeaders } from './generateAuthorizationHeaders.js'
|
|
|
6
6
|
import { FMError } from '../FMError.js';
|
|
7
7
|
import { Layout } from '../layouts/layout.js';
|
|
8
8
|
import fetch from 'node-fetch';
|
|
9
|
-
// @ts-
|
|
10
|
-
import fetchWithCookies, { CookieJar } from
|
|
9
|
+
// @ts-expect-error
|
|
10
|
+
import fetchWithCookies, { CookieJar } from 'node-fetch-cookies';
|
|
11
11
|
/**
|
|
12
12
|
* Represents a database connection.
|
|
13
13
|
* @template T - The structure of the database.
|
|
@@ -57,7 +57,6 @@ export class Database extends EventEmitter {
|
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
await _fetch.json();
|
|
60
|
-
// console.log(data)
|
|
61
60
|
this._token = '';
|
|
62
61
|
}
|
|
63
62
|
/**
|
|
@@ -71,8 +70,6 @@ export class Database extends EventEmitter {
|
|
|
71
70
|
async login(forceLogin = false) {
|
|
72
71
|
if (this.token !== '' && !forceLogin)
|
|
73
72
|
return;
|
|
74
|
-
console.log("RELOGGING IN");
|
|
75
|
-
console.trace();
|
|
76
73
|
// Reset cookies
|
|
77
74
|
this.cookies = new CookieJar();
|
|
78
75
|
await this.host.getMetadata();
|
|
@@ -81,7 +78,6 @@ export class Database extends EventEmitter {
|
|
|
81
78
|
return this.token;
|
|
82
79
|
}
|
|
83
80
|
else {
|
|
84
|
-
console.time();
|
|
85
81
|
const url = new URL(`${this.endpoint}/sessions`);
|
|
86
82
|
url.hostname = this.host.hostname;
|
|
87
83
|
const res = await fetch(url, {
|
|
@@ -94,7 +90,6 @@ export class Database extends EventEmitter {
|
|
|
94
90
|
})
|
|
95
91
|
})
|
|
96
92
|
});
|
|
97
|
-
console.time();
|
|
98
93
|
const _res = (await res.json());
|
|
99
94
|
if (res.status === 200) {
|
|
100
95
|
this._token = res.headers.get('x-fm-data-access-token') ?? '';
|
|
@@ -128,9 +123,9 @@ export class Database extends EventEmitter {
|
|
|
128
123
|
// options.headers.cookies = this._generateCookieHeader()
|
|
129
124
|
// console.log(options.headers.cookies)
|
|
130
125
|
// options.rejectUnauthorized = this.host.verify
|
|
131
|
-
const _fetch = options.useCookieJar
|
|
132
|
-
await fetchWithCookies(this.cookies, url, options)
|
|
133
|
-
await fetch(url, options);
|
|
126
|
+
const _fetch = options.useCookieJar
|
|
127
|
+
? await fetchWithCookies(this.cookies, url, options)
|
|
128
|
+
: await fetch(url, options);
|
|
134
129
|
// if (_fetch.headers.get('set-cookie')) {
|
|
135
130
|
// console.log(_fetch.headers.get('set-cookie'))
|
|
136
131
|
// for (const cookie of (_fetch.headers.get("Set-Cookie") ?? "").split("; ")) {
|
|
@@ -151,11 +146,11 @@ export class Database extends EventEmitter {
|
|
|
151
146
|
return _fetch;
|
|
152
147
|
}
|
|
153
148
|
_generateCookieHeader() {
|
|
154
|
-
|
|
155
|
-
for (
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const key of Object.keys(this.cookies)) {
|
|
156
151
|
out.push(`${key}=${this.cookies[key]}`);
|
|
157
152
|
}
|
|
158
|
-
return out.join(
|
|
153
|
+
return out.join('; ');
|
|
159
154
|
}
|
|
160
155
|
async _apiRequestJSON(url, options = {}) {
|
|
161
156
|
if (!options.headers)
|
|
@@ -12,9 +12,9 @@ import {type HostBase} from './HostBase.js'
|
|
|
12
12
|
import {type DatabaseBase} from './databaseBase.js'
|
|
13
13
|
import {type ApiLayout, type ApiResults} from '../models/apiResults.js'
|
|
14
14
|
import {type DatabaseStructure} from '../databaseStructure.js'
|
|
15
|
-
import fetch, {HeadersInit, RequestInfo, RequestInit, Response} from 'node-fetch'
|
|
16
|
-
// @ts-
|
|
17
|
-
import fetchWithCookies, {CookieJar} from
|
|
15
|
+
import fetch, {type HeadersInit, type RequestInfo, type RequestInit, type Response} from 'node-fetch'
|
|
16
|
+
// @ts-expect-error
|
|
17
|
+
import fetchWithCookies, {CookieJar} from 'node-fetch-cookies'
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Represents a database connection.
|
|
@@ -28,7 +28,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
28
28
|
readonly name: string
|
|
29
29
|
readonly debug: boolean
|
|
30
30
|
|
|
31
|
-
constructor(host: HostBase, conn: databaseOptionsWithExternalSources) {
|
|
31
|
+
constructor (host: HostBase, conn: databaseOptionsWithExternalSources) {
|
|
32
32
|
super()
|
|
33
33
|
this.host = host
|
|
34
34
|
this.name = conn.database
|
|
@@ -37,7 +37,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
40
|
-
private generateExternalSourceLogin(data: databaseOptionsBase) {
|
|
40
|
+
private generateExternalSourceLogin (data: databaseOptionsBase) {
|
|
41
41
|
if (data.credentials.method === 'filemaker') {
|
|
42
42
|
const _data = data.credentials
|
|
43
43
|
return {
|
|
@@ -45,8 +45,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
45
45
|
username: _data.username,
|
|
46
46
|
password: _data.password
|
|
47
47
|
}
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
48
|
+
} else {
|
|
50
49
|
throw new Error('Not yet supported login method')
|
|
51
50
|
}
|
|
52
51
|
}
|
|
@@ -58,7 +57,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
58
57
|
* @returns {Promise<void>} A promise that resolves with no value once the logout is successful.
|
|
59
58
|
* @throws {Error} Throws an error if the user is not logged in.
|
|
60
59
|
*/
|
|
61
|
-
async logout(): Promise<void> {
|
|
60
|
+
async logout (): Promise<void> {
|
|
62
61
|
if (this.token === '') throw new Error('Not logged in')
|
|
63
62
|
|
|
64
63
|
const _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
|
|
@@ -68,7 +67,6 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
68
67
|
}
|
|
69
68
|
})
|
|
70
69
|
await _fetch.json()
|
|
71
|
-
// console.log(data)
|
|
72
70
|
this._token = ''
|
|
73
71
|
}
|
|
74
72
|
|
|
@@ -80,11 +78,9 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
80
78
|
* @throws {FMError} - Throws an FMError if login fails.
|
|
81
79
|
* @return {Promise<string>} - Returns a promise that resolves to the access token upon successful login.
|
|
82
80
|
*/
|
|
83
|
-
async login(forceLogin = false) {
|
|
81
|
+
async login (forceLogin = false) {
|
|
84
82
|
if (this.token !== '' && !forceLogin) return
|
|
85
83
|
|
|
86
|
-
console.log("RELOGGING IN")
|
|
87
|
-
console.trace()
|
|
88
84
|
// Reset cookies
|
|
89
85
|
this.cookies = new CookieJar()
|
|
90
86
|
|
|
@@ -93,9 +89,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
93
89
|
if (this.connection_details.credentials.method === 'token') {
|
|
94
90
|
this._token = (this.connection_details.credentials).token
|
|
95
91
|
return this.token
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
console.time()
|
|
92
|
+
} else {
|
|
99
93
|
const url = new URL(`${this.endpoint}/sessions`)
|
|
100
94
|
url.hostname = this.host.hostname
|
|
101
95
|
const res = await fetch(url, {
|
|
@@ -108,13 +102,11 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
108
102
|
})
|
|
109
103
|
})
|
|
110
104
|
})
|
|
111
|
-
console.time()
|
|
112
105
|
const _res = (await res.json()) as any
|
|
113
106
|
if (res.status === 200) {
|
|
114
107
|
this._token = res.headers.get('x-fm-data-access-token') ?? ''
|
|
115
108
|
return this._token
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
109
|
+
} else {
|
|
118
110
|
throw new FMError(
|
|
119
111
|
_res.messages[0].code as string | number,
|
|
120
112
|
_res.status as number,
|
|
@@ -124,7 +116,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
124
116
|
}
|
|
125
117
|
}
|
|
126
118
|
|
|
127
|
-
get token() {
|
|
119
|
+
get token () {
|
|
128
120
|
return this._token
|
|
129
121
|
}
|
|
130
122
|
|
|
@@ -133,13 +125,13 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
133
125
|
*
|
|
134
126
|
* @returns {string} The endpoint URL.
|
|
135
127
|
*/
|
|
136
|
-
get endpoint(): string {
|
|
128
|
+
get endpoint (): string {
|
|
137
129
|
return `${this.host.hostname}/fmi/data/v2/databases/${this.name}`
|
|
138
130
|
}
|
|
139
131
|
|
|
140
|
-
async _apiRequestRaw(url: URL | RequestInfo, options: RequestInit & {
|
|
141
|
-
headers?: Record<string, string
|
|
142
|
-
useCookieJar?: boolean
|
|
132
|
+
async _apiRequestRaw (url: URL | RequestInfo, options: RequestInit & {
|
|
133
|
+
headers?: Record<string, string>
|
|
134
|
+
useCookieJar?: boolean
|
|
143
135
|
} = {}, autoRelogin = true): Promise<Response> {
|
|
144
136
|
if (this.debug) console.log(`EASYFM DEBUG: ${JSON.stringify(options)} ${url instanceof Request ? url.url : url}`)
|
|
145
137
|
if (this.token === '') await this.login(true)
|
|
@@ -152,9 +144,9 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
152
144
|
// options.rejectUnauthorized = this.host.verify
|
|
153
145
|
|
|
154
146
|
const _fetch: Response =
|
|
155
|
-
options.useCookieJar
|
|
156
|
-
await fetchWithCookies(this.cookies, url, options)
|
|
157
|
-
await fetch(url, options)
|
|
147
|
+
options.useCookieJar
|
|
148
|
+
? await fetchWithCookies(this.cookies, url, options)
|
|
149
|
+
: await fetch(url, options)
|
|
158
150
|
// if (_fetch.headers.get('set-cookie')) {
|
|
159
151
|
// console.log(_fetch.headers.get('set-cookie'))
|
|
160
152
|
// for (const cookie of (_fetch.headers.get("Set-Cookie") ?? "").split("; ")) {
|
|
@@ -170,16 +162,15 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
170
162
|
console.trace()
|
|
171
163
|
await this.login(true)
|
|
172
164
|
return await this._apiRequestRaw(url, options, false)
|
|
173
|
-
}
|
|
174
|
-
else return _fetch
|
|
165
|
+
} else return _fetch
|
|
175
166
|
}
|
|
176
167
|
|
|
177
|
-
private _generateCookieHeader() {
|
|
178
|
-
|
|
179
|
-
for (
|
|
168
|
+
private _generateCookieHeader () {
|
|
169
|
+
const out: string[] = []
|
|
170
|
+
for (const key of Object.keys(this.cookies)) {
|
|
180
171
|
out.push(`${key}=${this.cookies[key]}`)
|
|
181
172
|
}
|
|
182
|
-
return out.join(
|
|
173
|
+
return out.join('; ')
|
|
183
174
|
}
|
|
184
175
|
|
|
185
176
|
async _apiRequestJSON<T = any>(url: URL | RequestInfo, options: RequestInit & {
|
|
@@ -208,7 +199,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
208
199
|
* @returns {Promise<Layout[]>} A promise that resolves to an array of Layout objects.
|
|
209
200
|
* @throws {FMError} If there was an error retrieving the layouts.
|
|
210
201
|
*/
|
|
211
|
-
async listLayouts() {
|
|
202
|
+
async listLayouts () {
|
|
212
203
|
const req = await this._apiRequestJSON<{ layouts: ApiLayout[] }>(`${this.endpoint}/layouts?page=2`)
|
|
213
204
|
if (!req.response) throw new FMError(req.messages[0].code, req.httpStatus, req.messages[0].message)
|
|
214
205
|
|
|
@@ -225,11 +216,11 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
|
|
|
225
216
|
|
|
226
217
|
layout<R extends keyof T['layouts']>(name: R): Layout<T['layouts'][R]>
|
|
227
218
|
layout<R extends LayoutInterface>(name: string): Layout<R>
|
|
228
|
-
layout(name: string): Layout<any> {
|
|
219
|
+
layout (name: string): Layout<any> {
|
|
229
220
|
return new Layout<LayoutInterface>(this, name)
|
|
230
221
|
}
|
|
231
222
|
|
|
232
|
-
script(name: string, parameter = ''): Script {
|
|
223
|
+
script (name: string, parameter = ''): Script {
|
|
233
224
|
return ({name, parameter} satisfies Script)
|
|
234
225
|
}
|
|
235
226
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type HostBase } from './HostBase.js';
|
|
2
2
|
import { type ApiResults } from '../models/apiResults.js';
|
|
3
|
-
import { RequestInfo, RequestInit, Response } from
|
|
3
|
+
import { type RequestInfo, type RequestInit, type Response } from 'node-fetch';
|
|
4
4
|
export interface DatabaseBase {
|
|
5
5
|
host: HostBase;
|
|
6
6
|
readonly name: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import {type HostBase} from './HostBase.js'
|
|
6
6
|
import {type ApiResults} from '../models/apiResults.js'
|
|
7
|
-
import {RequestInfo, RequestInit, Response} from
|
|
7
|
+
import {type RequestInfo, type RequestInit, type Response} from 'node-fetch'
|
|
8
8
|
|
|
9
9
|
export interface DatabaseBase {
|
|
10
10
|
host: HostBase
|
package/src/records/field.d.ts
CHANGED
|
@@ -48,6 +48,12 @@ export declare class Field<T extends FieldValue> {
|
|
|
48
48
|
* @returns {Promise<void>} - A promise that resolves when the file is successfully uploaded.
|
|
49
49
|
*/
|
|
50
50
|
upload(file: File): Promise<void>;
|
|
51
|
-
stream(): Promise<
|
|
52
|
-
|
|
51
|
+
stream(): Promise<{
|
|
52
|
+
data: NodeJS.ReadableStream;
|
|
53
|
+
mime: string;
|
|
54
|
+
}>;
|
|
55
|
+
arrayBuffer(): Promise<{
|
|
56
|
+
data: ArrayBuffer;
|
|
57
|
+
mime: string;
|
|
58
|
+
}>;
|
|
53
59
|
}
|
package/src/records/field.js
CHANGED
|
@@ -41,7 +41,7 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
|
|
|
41
41
|
};
|
|
42
42
|
import { RecordTypes } from '../types.js';
|
|
43
43
|
import { ApiFieldDisplayTypes, ApiFieldResultTypes, ApiFieldTypes } from '../models/apiResults.js';
|
|
44
|
-
import { FMError } from
|
|
44
|
+
import { FMError } from '../FMError.js';
|
|
45
45
|
/**
|
|
46
46
|
* A class representing a field in a record.
|
|
47
47
|
*
|
|
@@ -58,11 +58,11 @@ let Field = (() => {
|
|
|
58
58
|
_private_streamAsync_decorators = [containerDownloadFunction];
|
|
59
59
|
_arrayBuffer_decorators = [containerDownloadFunction];
|
|
60
60
|
__esDecorate(this, _private_streamAsync_descriptor = { value: __setFunctionName(async function () {
|
|
61
|
-
|
|
61
|
+
const req = await this.parent.layout.database._apiRequestRaw(this.string, { useCookieJar: true });
|
|
62
62
|
if (!req.ok || !req.body) {
|
|
63
63
|
throw new Error(`HTTP Error: ${req.status} (${req.statusText})`);
|
|
64
64
|
}
|
|
65
|
-
return req.body;
|
|
65
|
+
return { data: req.body, mime: req.headers.get("Content-Type") ?? '' };
|
|
66
66
|
}, "#streamAsync") }, _private_streamAsync_decorators, { kind: "method", name: "#streamAsync", static: false, private: true, access: { has: obj => #streamAsync in obj, get: obj => obj.#streamAsync }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
67
67
|
__esDecorate(this, null, _arrayBuffer_decorators, { kind: "method", name: "arrayBuffer", static: false, private: false, access: { has: obj => "arrayBuffer" in obj, get: obj => obj.arrayBuffer }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
68
68
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
@@ -178,7 +178,6 @@ let Field = (() => {
|
|
|
178
178
|
}
|
|
179
179
|
const form = new FormData();
|
|
180
180
|
form.append('upload', file);
|
|
181
|
-
console.log(`ENDPOINT: ${this.parent.endpoint}/containers/${this.id}/1`);
|
|
182
181
|
const res = await this.parent.layout.database._apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
|
|
183
182
|
method: 'POST',
|
|
184
183
|
headers: {
|
|
@@ -188,44 +187,39 @@ let Field = (() => {
|
|
|
188
187
|
body: form
|
|
189
188
|
});
|
|
190
189
|
if (!res.ok) {
|
|
191
|
-
console.log("RESULT:", await res.text());
|
|
192
190
|
throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`);
|
|
193
191
|
}
|
|
194
192
|
const data = await res.json();
|
|
195
|
-
if (data.messages[0].code ===
|
|
193
|
+
if (data.messages[0].code === '0')
|
|
196
194
|
return;
|
|
197
195
|
else {
|
|
198
|
-
console.log(data);
|
|
199
196
|
throw new FMError(data.messages[0].code, res.status, res);
|
|
200
197
|
}
|
|
201
198
|
}
|
|
202
199
|
get #streamAsync() { return _private_streamAsync_descriptor.value; }
|
|
203
|
-
stream() {
|
|
204
|
-
return this.#streamAsync();
|
|
200
|
+
async stream() {
|
|
201
|
+
return await this.#streamAsync();
|
|
205
202
|
}
|
|
206
203
|
async arrayBuffer() {
|
|
207
|
-
console.log(this.string);
|
|
208
204
|
const req = await this.parent.layout.database._apiRequestRaw(this.string, { useCookieJar: true });
|
|
209
205
|
if (!req.ok)
|
|
210
206
|
throw new Error(`HTTP Error: ${req.status} (${req.statusText})`);
|
|
211
|
-
|
|
212
|
-
return await req.arrayBuffer();
|
|
207
|
+
return { data: await req.arrayBuffer(), mime: req.headers.get("Content-Type") ?? '' };
|
|
213
208
|
}
|
|
214
209
|
};
|
|
215
210
|
})();
|
|
216
211
|
export { Field };
|
|
217
212
|
function containerDownloadFunction(originalMethod, context) {
|
|
218
213
|
async function replacementMethod(...args) {
|
|
219
|
-
return ContainerDownloadExecutor.processTask(() => originalMethod.call(this, ...args));
|
|
214
|
+
return await ContainerDownloadExecutor.processTask(async () => await originalMethod.call(this, ...args));
|
|
220
215
|
}
|
|
221
216
|
return replacementMethod;
|
|
222
217
|
}
|
|
223
218
|
class ContainerDownloadExecutorClass {
|
|
224
219
|
hasDownloadedFirstContainer = 0;
|
|
225
220
|
waitingFuncs = [];
|
|
226
|
-
constructor() { }
|
|
227
221
|
async processTask(task) {
|
|
228
|
-
|
|
222
|
+
const isFirstTask = this.hasDownloadedFirstContainer === 0;
|
|
229
223
|
if (isFirstTask) {
|
|
230
224
|
this.hasDownloadedFirstContainer = 1;
|
|
231
225
|
let res;
|
|
@@ -234,7 +228,7 @@ class ContainerDownloadExecutorClass {
|
|
|
234
228
|
}
|
|
235
229
|
catch (e) {
|
|
236
230
|
this.hasDownloadedFirstContainer = 0;
|
|
237
|
-
|
|
231
|
+
const nextTask = this.waitingFuncs.splice(0, 1)[0];
|
|
238
232
|
if (nextTask) {
|
|
239
233
|
this.processTask(nextTask.func)
|
|
240
234
|
.then(res => nextTask.done(res))
|
|
@@ -243,7 +237,7 @@ class ContainerDownloadExecutorClass {
|
|
|
243
237
|
throw e;
|
|
244
238
|
}
|
|
245
239
|
this.hasDownloadedFirstContainer = 2;
|
|
246
|
-
for (
|
|
240
|
+
for (const task of this.waitingFuncs) {
|
|
247
241
|
task.func()
|
|
248
242
|
.then((res) => task.done(res))
|
|
249
243
|
.catch((e) => task.err(e));
|
|
@@ -251,7 +245,7 @@ class ContainerDownloadExecutorClass {
|
|
|
251
245
|
return res;
|
|
252
246
|
}
|
|
253
247
|
else if (this.hasDownloadedFirstContainer === 1) {
|
|
254
|
-
return new Promise((resolve, reject) => {
|
|
248
|
+
return await new Promise((resolve, reject) => {
|
|
255
249
|
this.waitingFuncs.push({
|
|
256
250
|
func: task,
|
|
257
251
|
done: resolve,
|
package/src/records/field.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {RecordTypes} from '../types.js'
|
|
|
6
6
|
import {ApiFieldDisplayTypes, type ApiFieldMetadata, ApiFieldResultTypes, ApiFieldTypes} from '../models/apiResults.js'
|
|
7
7
|
import {type LayoutBase} from '../layouts/layoutBase.js'
|
|
8
8
|
import {type Moment} from 'moment'
|
|
9
|
-
import {FMError} from
|
|
9
|
+
import {FMError} from '../FMError.js'
|
|
10
10
|
|
|
11
11
|
export type FieldValue = string | number | Moment | Container
|
|
12
12
|
export type Container = null
|
|
@@ -31,7 +31,7 @@ export class Field<T extends FieldValue> {
|
|
|
31
31
|
|
|
32
32
|
static firstContainerDownload: Promise<any> | null = null
|
|
33
33
|
|
|
34
|
-
constructor(record: Parentable, id: string, contents: T) {
|
|
34
|
+
constructor (record: Parentable, id: string, contents: T) {
|
|
35
35
|
this.parent = record
|
|
36
36
|
this.id = id
|
|
37
37
|
this._value = contents
|
|
@@ -44,13 +44,13 @@ export class Field<T extends FieldValue> {
|
|
|
44
44
|
* @param {T | null} content - The new content value to be set. Can be either the type T or null.
|
|
45
45
|
* @throws {Error} Cannot set container value using set(). Use upload() instead, if the result is a 'container'.
|
|
46
46
|
*/
|
|
47
|
-
set(content: T | null) {
|
|
47
|
+
set (content: T | null) {
|
|
48
48
|
if (this.metadata.result === 'container') throw new Error('Cannot set container value using set(). Use upload() instead.')
|
|
49
49
|
else this._value = content
|
|
50
50
|
this.edited = true
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
get metadata(): ApiFieldMetadata {
|
|
53
|
+
get metadata (): ApiFieldMetadata {
|
|
54
54
|
if (!this.parent.layout.metadata) {
|
|
55
55
|
// Default to a regular text field
|
|
56
56
|
return {
|
|
@@ -88,8 +88,7 @@ export class Field<T extends FieldValue> {
|
|
|
88
88
|
repetitions: 1,
|
|
89
89
|
valueList: ''
|
|
90
90
|
}
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
91
|
+
} else {
|
|
93
92
|
return this.parent.layout.metadata.fieldMetaData.find(i => i.name === this.id) ?? {
|
|
94
93
|
name: this.id.toString(),
|
|
95
94
|
type: ApiFieldTypes.NORMAL,
|
|
@@ -109,16 +108,16 @@ export class Field<T extends FieldValue> {
|
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
get value(): T | null {
|
|
111
|
+
get value (): T | null {
|
|
113
112
|
// if (this.metadata.result === "container") throw "Use await field.stream() to get the contents of a container field, instead of field.value"
|
|
114
113
|
return this._value
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
set value(value: T) {
|
|
116
|
+
set value (value: T) {
|
|
118
117
|
this._value = value
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
get string(): string {
|
|
120
|
+
get string (): string {
|
|
122
121
|
if (typeof this._value === 'string') {
|
|
123
122
|
return this._value
|
|
124
123
|
}
|
|
@@ -135,14 +134,13 @@ export class Field<T extends FieldValue> {
|
|
|
135
134
|
*
|
|
136
135
|
* @returns {Promise<void>} - A promise that resolves when the file is successfully uploaded.
|
|
137
136
|
*/
|
|
138
|
-
async upload(file: File): Promise<void> {
|
|
137
|
+
async upload (file: File): Promise<void> {
|
|
139
138
|
if (this.metadata.result !== 'container') {
|
|
140
139
|
throw new Error('Cannot upload a file to the field; ' + this.id + ' (not a container field)')
|
|
141
140
|
}
|
|
142
141
|
const form = new FormData()
|
|
143
142
|
form.append('upload', file)
|
|
144
143
|
|
|
145
|
-
console.log(`ENDPOINT: ${this.parent.endpoint}/containers/${this.id}/1`)
|
|
146
144
|
const res = await this.parent.layout.database._apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
|
|
147
145
|
method: 'POST',
|
|
148
146
|
headers: {
|
|
@@ -152,43 +150,39 @@ export class Field<T extends FieldValue> {
|
|
|
152
150
|
body: form
|
|
153
151
|
})
|
|
154
152
|
if (!res.ok) {
|
|
155
|
-
console.log("RESULT:", await res.text())
|
|
156
153
|
throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`)
|
|
157
154
|
}
|
|
158
155
|
const data: { messages: Array<{ code: string }> } = await res.json() as any
|
|
159
|
-
if (data.messages[0].code ===
|
|
156
|
+
if (data.messages[0].code === '0') return
|
|
160
157
|
else {
|
|
161
|
-
console.log(data)
|
|
162
158
|
throw new FMError(data.messages[0].code, res.status, res)
|
|
163
159
|
}
|
|
164
160
|
}
|
|
165
161
|
|
|
166
162
|
@containerDownloadFunction
|
|
167
|
-
async #streamAsync() {
|
|
168
|
-
|
|
163
|
+
async #streamAsync (): Promise<{data: NodeJS.ReadableStream, mime: string}> {
|
|
164
|
+
const req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
|
|
169
165
|
if (!req.ok || !req.body) {
|
|
170
166
|
throw new Error(`HTTP Error: ${req.status} (${req.statusText})`)
|
|
171
167
|
}
|
|
172
|
-
return req.body
|
|
168
|
+
return {data: req.body, mime: req.headers.get("Content-Type") ?? ''}
|
|
173
169
|
}
|
|
174
170
|
|
|
175
|
-
stream() {
|
|
176
|
-
return this.#streamAsync()
|
|
171
|
+
async stream () {
|
|
172
|
+
return await this.#streamAsync()
|
|
177
173
|
}
|
|
178
174
|
|
|
179
175
|
@containerDownloadFunction
|
|
180
|
-
async arrayBuffer() {
|
|
181
|
-
console.log(this.string)
|
|
176
|
+
async arrayBuffer (): Promise<{data: ArrayBuffer, mime: string}> {
|
|
182
177
|
const req = await this.parent.layout.database._apiRequestRaw(this.string, {useCookieJar: true})
|
|
183
178
|
if (!req.ok) throw new Error(`HTTP Error: ${req.status} (${req.statusText})`)
|
|
184
|
-
|
|
185
|
-
return await req.arrayBuffer()
|
|
179
|
+
return {data: await req.arrayBuffer(), mime: req.headers.get("Content-Type") ?? ''}
|
|
186
180
|
}
|
|
187
181
|
}
|
|
188
182
|
|
|
189
|
-
function containerDownloadFunction<T extends FieldValue, R = unknown>(originalMethod: (...p:
|
|
190
|
-
async function replacementMethod(this: Field<T>, ...args:
|
|
191
|
-
return ContainerDownloadExecutor.processTask(() => originalMethod.call(this, ...args))
|
|
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))
|
|
192
186
|
}
|
|
193
187
|
|
|
194
188
|
return replacementMethod
|
|
@@ -196,24 +190,22 @@ function containerDownloadFunction<T extends FieldValue, R = unknown>(originalMe
|
|
|
196
190
|
|
|
197
191
|
class ContainerDownloadExecutorClass {
|
|
198
192
|
hasDownloadedFirstContainer: 0 | 1 | 2 = 0
|
|
199
|
-
waitingFuncs: {
|
|
200
|
-
func: (() => any)
|
|
201
|
-
done: ((...args: any[]) => any)
|
|
202
|
-
err: ((e:
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
constructor() {}
|
|
193
|
+
waitingFuncs: Array<{
|
|
194
|
+
func: (() => any)
|
|
195
|
+
done: ((...args: any[]) => any)
|
|
196
|
+
err: ((e: any) => any)
|
|
197
|
+
}> = []
|
|
206
198
|
|
|
207
199
|
async processTask<T extends () => Promise<any>>(task: T) {
|
|
208
|
-
|
|
200
|
+
const isFirstTask = this.hasDownloadedFirstContainer === 0
|
|
209
201
|
if (isFirstTask) {
|
|
210
202
|
this.hasDownloadedFirstContainer = 1
|
|
211
203
|
let res
|
|
212
204
|
try {
|
|
213
205
|
res = await task()
|
|
214
|
-
} catch(e) {
|
|
206
|
+
} catch (e) {
|
|
215
207
|
this.hasDownloadedFirstContainer = 0
|
|
216
|
-
|
|
208
|
+
const nextTask = this.waitingFuncs.splice(0, 1)[0]
|
|
217
209
|
if (nextTask) {
|
|
218
210
|
this.processTask(nextTask.func)
|
|
219
211
|
.then(res => nextTask.done(res))
|
|
@@ -222,24 +214,22 @@ class ContainerDownloadExecutorClass {
|
|
|
222
214
|
throw e
|
|
223
215
|
}
|
|
224
216
|
this.hasDownloadedFirstContainer = 2
|
|
225
|
-
for (
|
|
217
|
+
for (const task of this.waitingFuncs) {
|
|
226
218
|
task.func()
|
|
227
219
|
.then((res: any) => task.done(res))
|
|
228
220
|
.catch((e: any) => task.err(e))
|
|
229
221
|
}
|
|
230
222
|
return res
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return new Promise((resolve, reject) => {
|
|
223
|
+
} else if (this.hasDownloadedFirstContainer === 1) {
|
|
224
|
+
return await new Promise((resolve, reject) => {
|
|
234
225
|
this.waitingFuncs.push({
|
|
235
226
|
func: task,
|
|
236
227
|
done: resolve,
|
|
237
228
|
err: reject
|
|
238
229
|
})
|
|
239
230
|
})
|
|
240
|
-
}
|
|
241
|
-
else return await task()
|
|
231
|
+
} else return await task()
|
|
242
232
|
}
|
|
243
233
|
}
|
|
244
234
|
|
|
245
|
-
const ContainerDownloadExecutor = new ContainerDownloadExecutorClass()
|
|
235
|
+
const ContainerDownloadExecutor = new ContainerDownloadExecutorClass()
|
|
@@ -153,7 +153,6 @@ export class LayoutRecord extends RecordBase {
|
|
|
153
153
|
const res = await this.layout.database._apiRequestJSON(this.endpoint, {
|
|
154
154
|
method: 'DELETE'
|
|
155
155
|
});
|
|
156
|
-
console.log(res);
|
|
157
156
|
if (typeof res.response?.scriptError !== 'undefined' && res.response?.scriptError !== '0') {
|
|
158
157
|
throw new FMError(res.response.scriptError, res.httpStatus, res);
|
|
159
158
|
}
|
|
@@ -177,7 +177,6 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
|
|
|
177
177
|
const res = await this.layout.database._apiRequestJSON(this.endpoint, {
|
|
178
178
|
method: 'DELETE'
|
|
179
179
|
})
|
|
180
|
-
console.log(res)
|
|
181
180
|
if (typeof res.response?.scriptError !== 'undefined' && res.response?.scriptError !== '0') {
|
|
182
181
|
throw new FMError(res.response.scriptError, res.httpStatus, res)
|
|
183
182
|
} else if (res.messages[0].code === '0') {
|