@jd-data-limited/easy-fm 4.0.5 → 4.0.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/README.md CHANGED
@@ -1,30 +1,38 @@
1
- # easy-fm
1
+ # Introduction
2
2
 
3
- A [FileMaker Data API](https://help.claris.com/en/data-api-guide/content/index.html) client for NodeJS
3
+ A FileMaker Data API client for NodeJS
4
4
 
5
5
  easy-fm is a Node.js module that allows you to interact with
6
- a [FileMaker database](https://www.claris.com/filemaker/) stored on a [FileMaker server](https://www.claris.com/filemaker/server/)
7
- or [FileMaker Cloud](https://store.claris.com/filemaker-cloud). This module interacts with your server using the
8
- [FileMaker Data API](https://help.claris.com/en/data-api-guide/content/index.html).
9
- <!-- TOC -->
6
+ a FileMaker database stored on a FileMaker server
7
+ or FileMaker Cloud. This module interacts with your server using the
8
+ FileMaker Data API.
9
+
10
+ # Contents
10
11
 
11
- * [easy-fm](#easy-fm)
12
- * [FileMaker setup instructions](#filemaker-setup-instructions)
13
- * [Before you begin](#before-you-begin)
14
- * [Connecting to a database](#connecting-to-a-database)
15
- * [Getting records](#getting-records)
16
- * [Fetch a range of records](#fetch-a-range-of-records)
17
- * [Searching for records](#searching-for-records)
18
- * [Fetch a record using its record ID (NOT RECOMMENDED)](#fetch-a-record-using-its-record-id-not-recommended)
19
- * [Create a record](#create-a-record)
20
- * [Modify a record](#modify-a-record)
12
+ <!-- TOC -->
13
+ * [Introduction](#introduction)
14
+ * [Contents](#contents)
15
+ * [Installation](#installation)
16
+ * [Usage](#usage)
17
+ * [Connecting to a database](#connecting-to-a-database)
18
+ * [Getting records](#getting-records)
19
+ * [Fetch a range of records](#fetch-a-range-of-records)
20
+ * [Searching for records](#searching-for-records)
21
+ * [Fetch a record using its record ID (NOT RECOMMENDED)](#fetch-a-record-using-its-record-id-not-recommended)
22
+ * [Create a record](#create-a-record)
23
+ * [Modify a record](#modify-a-record)
21
24
  * [Field names](#field-names)
22
25
  * [Portal names](#portal-names)
23
26
  * [Typescript Implementation](#typescript-implementation)
24
-
25
27
  <!-- TOC -->
26
28
 
27
- ## FileMaker setup instructions
29
+ # Installation
30
+
31
+ ```npm
32
+ npm install @jd-data-limited/easy-fm --save
33
+ ```
34
+
35
+ easy-fm also requires the following to be configured within your FileMaker enviroment:
28
36
 
29
37
  1. Enable the FileMaker Data API from the server's admin console. This setting is located
30
38
  in `Connectors > FileMaker Data API`.
@@ -33,7 +41,7 @@ or [FileMaker Cloud](https://store.claris.com/filemaker-cloud). This module inte
33
41
 
34
42
  ---
35
43
 
36
- ## Before you begin
44
+ # Usage
37
45
 
38
46
  - You need to know what your server's UTC time offset (in minutes) is.
39
47
  - Running `0 - (new Date()).getTimezoneOffset()` in javascript will give you the UTC time offset for your current
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@jd-data-limited/easy-fm",
3
- "version": "4.0.5",
3
+ "version": "4.0.8",
4
4
  "dependencies": {
5
5
  "follow-redirects": "^1.15.5",
6
+ "@types/follow-redirects": "^1.14.4",
6
7
  "moment": "^2.29.4"
7
8
  },
8
9
  "type": "module",
@@ -35,7 +36,6 @@
35
36
  "devDependencies": {
36
37
  "@types/btoa": "^1.2.3",
37
38
  "@types/events": "^3.0.0",
38
- "@types/follow-redirects": "^1.14.4",
39
39
  "@types/inquirer": "^9.0.7",
40
40
  "dotenv": "^16.3.1",
41
41
  "inquirer": "^9.2.13",
@@ -18,10 +18,11 @@ export declare class Database<T extends DatabaseStructure> extends EventEmitter
18
18
  constructor(host: HostBase, conn: databaseOptionsWithExternalSources);
19
19
  private generateExternalSourceLogin;
20
20
  logout(): Promise<void>;
21
- login(): Promise<any>;
21
+ login(forceLogin?: boolean): Promise<any>;
22
22
  get token(): any;
23
23
  get endpoint(): string;
24
- apiRequest<T = any>(url: string | Request, options?: any, autoRelogin?: boolean): Promise<ApiResults<T>>;
24
+ apiRequestRaw(url: string | Request, options?: any, autoRelogin?: boolean): Promise<Response>;
25
+ apiRequestJSON<T = any>(url: string | Request, options?: any): Promise<ApiResults<T>>;
25
26
  listLayouts(): Promise<Layout<LayoutInterface>[]>;
26
27
  getLayout<R extends keyof T["layouts"]>(name: R): Layout<T["layouts"][R]>;
27
28
  getLayout<R extends LayoutInterface>(name: string): Layout<R>;
@@ -44,11 +44,11 @@ export class Database extends EventEmitter {
44
44
  // console.log(data)
45
45
  this._token = null;
46
46
  }
47
- async login() {
47
+ async login(forceLogin = false) {
48
48
  // Reset cookies
49
49
  this.cookies = {};
50
50
  await this.host.getMetadata();
51
- if (this.token)
51
+ if (this.token && !forceLogin)
52
52
  throw new Error("Already logged in. Run logout() first");
53
53
  if (this.connection_details.credentials.method === "token") {
54
54
  this._token = this.connection_details.credentials.token;
@@ -85,10 +85,11 @@ export class Database extends EventEmitter {
85
85
  get endpoint() {
86
86
  return `${this.host.hostname}/fmi/data/v2/databases/${this.name}`;
87
87
  }
88
- async apiRequest(url, options = {}, autoRelogin = true) {
88
+ async apiRequestRaw(url, options = {}, autoRelogin = true) {
89
+ if (!this.token)
90
+ await this.login(true);
89
91
  if (!options.headers)
90
92
  options.headers = {};
91
- options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json";
92
93
  options.headers["authorization"] = "Bearer " + this._token;
93
94
  options.rejectUnauthorized = this.host.verify;
94
95
  let _fetch = await fetch(url, options);
@@ -98,24 +99,30 @@ export class Database extends EventEmitter {
98
99
  this.cookies[cookie_split[0]] = cookie_split[1];
99
100
  }
100
101
  }
102
+ if (_fetch.status === 401 && autoRelogin) {
103
+ await this.login(true);
104
+ return await this.apiRequestRaw(url, options, false);
105
+ }
106
+ return _fetch;
107
+ }
108
+ async apiRequestJSON(url, options = {}) {
109
+ if (!options.headers)
110
+ options.headers = {};
111
+ options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json";
112
+ let _fetch = await this.apiRequestRaw(url, options);
101
113
  let data = await _fetch.json();
102
114
  // Remove response if it is empty. This makes checking for an empty response easier
103
115
  if (data.response && Object.keys(data.response).length === 0)
104
116
  delete data.response;
105
117
  // console.log(data.messages[0])
106
- if (data.messages[0].code === "952" && autoRelogin) {
107
- this._token = null;
108
- await this.login();
109
- return await this.apiRequest(url, options, false);
110
- }
111
- else if (data.messages[0].code !== "0") {
118
+ if (data.messages[0].code !== "0") {
112
119
  throw new FMError(data.messages[0].code, _fetch.status, data);
113
120
  }
114
121
  data.httpStatus = _fetch.status;
115
122
  return data;
116
123
  }
117
124
  async listLayouts() {
118
- let req = await this.apiRequest(`${this.endpoint}/layouts?page=2`);
125
+ let req = await this.apiRequestJSON(`${this.endpoint}/layouts?page=2`);
119
126
  if (!req.response)
120
127
  throw new FMError(req.messages[0].code, req.httpStatus, req.messages[0].message);
121
128
  const cycleLayoutNames = (layouts) => {
@@ -65,12 +65,12 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
65
65
  this._token = null
66
66
  }
67
67
 
68
- async login() {
68
+ async login(forceLogin = false) {
69
69
  // Reset cookies
70
70
  this.cookies = {}
71
71
 
72
72
  await this.host.getMetadata()
73
- if (this.token) throw new Error("Already logged in. Run logout() first")
73
+ if (this.token && !forceLogin) throw new Error("Already logged in. Run logout() first")
74
74
 
75
75
  if (this.connection_details.credentials.method === "token") {
76
76
  this._token = (<loginOptionsToken>this.connection_details.credentials).token
@@ -110,9 +110,10 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
110
110
  return `${this.host.hostname}/fmi/data/v2/databases/${this.name}`
111
111
  }
112
112
 
113
- async apiRequest<T = any>(url: string | Request, options: any = {}, autoRelogin = true): Promise<ApiResults<T>> {
113
+ async apiRequestRaw(url: string | Request, options: any = {}, autoRelogin = true): Promise<Response> {
114
+ if (!this.token) await this.login(true)
115
+
114
116
  if (!options.headers) options.headers = {}
115
- options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json"
116
117
  options.headers["authorization"] = "Bearer " + this._token
117
118
  options.rejectUnauthorized = this.host.verify
118
119
 
@@ -123,18 +124,24 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
123
124
  this.cookies[cookie_split[0]] = cookie_split[1]
124
125
  }
125
126
  }
127
+
128
+ if (_fetch.status === 401 && autoRelogin) {
129
+ await this.login(true)
130
+ return await this.apiRequestRaw(url, options, false)
131
+ }
132
+ return _fetch
133
+ }
134
+ async apiRequestJSON<T = any>(url: string | Request, options: any = {}): Promise<ApiResults<T>> {
135
+ if (!options.headers) options.headers = {}
136
+ options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json"
137
+ let _fetch = await this.apiRequestRaw(url, options)
126
138
  let data = await _fetch.json() as ApiResults<T>
127
139
 
128
140
  // Remove response if it is empty. This makes checking for an empty response easier
129
141
  if (data.response && Object.keys(data.response).length === 0) delete data.response
130
142
 
131
143
  // console.log(data.messages[0])
132
- if (data.messages[0].code === "952" && autoRelogin) {
133
- this._token = null
134
- await this.login()
135
- return await this.apiRequest(url, options, false)
136
- }
137
- else if (data.messages[0].code !== "0") {
144
+ if (data.messages[0].code !== "0") {
138
145
  throw new FMError(data.messages[0].code, _fetch.status, data)
139
146
  }
140
147
 
@@ -143,7 +150,7 @@ export class Database<T extends DatabaseStructure> extends EventEmitter implemen
143
150
  }
144
151
 
145
152
  async listLayouts() {
146
- let req = await this.apiRequest<{layouts: ApiLayout[]}>(`${this.endpoint}/layouts?page=2`)
153
+ let req = await this.apiRequestJSON<{layouts: ApiLayout[]}>(`${this.endpoint}/layouts?page=2`)
147
154
  if (!req.response) throw new FMError(req.messages[0].code, req.httpStatus, req.messages[0].message)
148
155
 
149
156
  const cycleLayoutNames = (layouts: ApiLayout[]) => {
@@ -6,6 +6,7 @@ export interface DatabaseBase {
6
6
  host: HostBase;
7
7
  readonly name: string;
8
8
  endpoint: string;
9
- apiRequest<T = unknown>(url: string | Request, options?: any, autoRelogin?: boolean): Promise<ApiResults<T>>;
9
+ apiRequestJSON<T = unknown>(url: string | Request, options?: RequestInit | undefined, autoRelogin?: boolean): Promise<ApiResults<T>>;
10
+ apiRequestRaw(url: string | Request, options?: RequestInit | undefined): Promise<Response>;
10
11
  streamURL(url: string): Promise<IncomingMessage>;
11
12
  }
@@ -13,6 +13,7 @@ export interface DatabaseBase {
13
13
 
14
14
  // layouts: DatabaseStructure["layouts"]
15
15
 
16
- apiRequest<T = unknown>(url: string | Request, options?: any, autoRelogin?: boolean): Promise<ApiResults<T>>
16
+ apiRequestJSON<T = unknown>(url: string | Request, options?: RequestInit | undefined, autoRelogin?: boolean): Promise<ApiResults<T>>
17
+ apiRequestRaw(url: string | Request, options?: RequestInit | undefined): Promise<Response>
17
18
  streamURL(url: string): Promise<IncomingMessage>
18
19
  }
@@ -19,8 +19,7 @@ export class Layout {
19
19
  let url = `${this.endpoint}/script/${encodeURIComponent(script.name)}`;
20
20
  if (script.parameter)
21
21
  url += "?script.param=" + encodeURIComponent(script.parameter);
22
- let res = await this.database.apiRequest(url, {
23
- port: 443,
22
+ let res = await this.database.apiRequestJSON(url, {
24
23
  method: "GET"
25
24
  });
26
25
  if (res.response && res.messages[0].code === "0") {
@@ -38,7 +37,7 @@ export class Layout {
38
37
  if (this.metadata) {
39
38
  return this.metadata;
40
39
  }
41
- let res = await this.database.apiRequest(this.endpoint);
40
+ let res = await this.database.apiRequestJSON(this.endpoint);
42
41
  if (!res.response)
43
42
  throw new FMError(res.messages[0].code, res.httpStatus, res);
44
43
  this.metadata = res.response;
@@ -29,8 +29,7 @@ export class Layout<T extends LayoutInterface> implements LayoutBase {
29
29
  async runScript(script: Script): Promise<ScriptResult> {
30
30
  let url = `${this.endpoint}/script/${encodeURIComponent(script.name)}`
31
31
  if (script.parameter) url += "?script.param=" + encodeURIComponent(script.parameter)
32
- let res = await this.database.apiRequest<ApiScriptResult>(url, {
33
- port: 443,
32
+ let res = await this.database.apiRequestJSON<ApiScriptResult>(url, {
34
33
  method: "GET"
35
34
  })
36
35
  if (res.response && res.messages[0].code === "0") {
@@ -49,7 +48,7 @@ export class Layout<T extends LayoutInterface> implements LayoutBase {
49
48
  return this.metadata
50
49
  }
51
50
 
52
- let res = await this.database.apiRequest<ApiLayoutMetadata>(this.endpoint)
51
+ let res = await this.database.apiRequestJSON<ApiLayoutMetadata>(this.endpoint)
53
52
  if (!res.response) throw new FMError(res.messages[0].code, res.httpStatus, res)
54
53
  this.metadata = res.response
55
54
  return this.metadata
@@ -120,12 +120,13 @@ export class FieldBase {
120
120
  throw "Cannot upload a file to the field; " + this.id + " (not a container field)";
121
121
  let form = new FormData();
122
122
  form.append("upload", new File([buffer], filename, { type: mime }));
123
- await this.parent.layout.database.apiRequest(`${this.parent.endpoint}/containers/${this.id}/1`, {
123
+ let res = await this.parent.layout.database.apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
124
124
  method: "POST",
125
125
  // @ts-ignore
126
- headers: { "Authorization": "Bearer " + this.parent.layout.database.token },
127
126
  body: form
128
127
  });
128
+ if (!res.ok)
129
+ throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`);
129
130
  }
130
131
  async download(mode = DOWNLOAD_MODES.Stream) {
131
132
  if (this.metadata.result !== ApiFieldResultTypes.CONTAINER)
@@ -136,12 +136,12 @@ export class FieldBase<T extends FieldValue> {
136
136
  let form = new FormData()
137
137
  form.append("upload", new File([buffer], filename, {type: mime}))
138
138
 
139
- await this.parent.layout.database.apiRequest<null>(`${this.parent.endpoint}/containers/${this.id}/1`, {
139
+ let res = await this.parent.layout.database.apiRequestRaw(`${this.parent.endpoint}/containers/${this.id}/1`, {
140
140
  method: "POST",
141
141
  // @ts-ignore
142
- headers: {"Authorization": "Bearer " + this.parent.layout.database.token},
143
142
  body: form
144
143
  })
144
+ if (!res.ok) throw new Error(`Upload failed with HTTP error: ${res.status} (${res.statusText})`)
145
145
  }
146
146
 
147
147
  download(mode: DOWNLOAD_MODES.Stream): Promise<http.IncomingMessage>
@@ -2,9 +2,10 @@ import { LayoutInterface } from "../../layouts/layoutInterface.js";
2
2
  import { PickPortals, ScriptRequestData } from "../../types.js";
3
3
  import { LayoutBase } from "../../layouts/layoutBase.js";
4
4
  import { LayoutRecord } from "../layoutRecord.js";
5
+ import { Query } from "../../utils/query.js";
5
6
  export type SortOrder = "ascend" | "descend";
6
7
  export type FindRequest = {
7
- [key: string]: string;
8
+ [key: string]: Query;
8
9
  } & {
9
10
  omit?: "true" | "false";
10
11
  };
@@ -35,6 +36,7 @@ export declare class RecordGetOperation<T extends LayoutInterface, OPTIONS exten
35
36
  protected queries: FindRequest[];
36
37
  constructor(layout: LayoutBase, options: OPTIONS);
37
38
  get isFindRequest(): boolean;
39
+ private formatQueries;
38
40
  protected generateParamsBody(offset: number, limit: number): {
39
41
  [key: string]: any;
40
42
  };
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { LayoutRecord } from "../layoutRecord.js";
5
5
  import { FMError } from "../../FMError.js";
6
+ import { FindRequestSymbol } from "../../utils/query.js";
6
7
  export class RecordGetOperation {
7
8
  layout;
8
9
  limit = 100;
@@ -21,6 +22,19 @@ export class RecordGetOperation {
21
22
  get isFindRequest() {
22
23
  return this.queries.length !== 0;
23
24
  }
25
+ formatQueries() {
26
+ return this.queries.map(query => {
27
+ let out = {};
28
+ for (let key of Object.keys(query)) {
29
+ if (query[key][FindRequestSymbol])
30
+ out[key] = query[key][FindRequestSymbol];
31
+ else {
32
+ out[key] = query[key];
33
+ }
34
+ }
35
+ return out;
36
+ });
37
+ }
24
38
  generateParamsBody(offset, limit) {
25
39
  const params = {
26
40
  limit: limit.toString(),
@@ -41,7 +55,7 @@ export class RecordGetOperation {
41
55
  if (this.scriptData.prerequest?.parameter)
42
56
  params["script.prerequest.param"] = this.scriptData.prerequest.parameter;
43
57
  if (this.queries.length !== 0)
44
- params["query"] = this.queries;
58
+ params["query"] = this.formatQueries();
45
59
  let portals = Object.keys(this.portals);
46
60
  params["portal"] = portals;
47
61
  for (let portal of portals) {
@@ -105,7 +119,7 @@ export class RecordGetOperation {
105
119
  body: is_find ? JSON.stringify(this.generateParamsBody(offset, limit)) : undefined,
106
120
  };
107
121
  try {
108
- let res = await this.layout.database.apiRequest(endpoint, reqData);
122
+ let res = await this.layout.database.apiRequestJSON(endpoint, reqData);
109
123
  if (res.messages[0].code === "0" && res.response) {
110
124
  // console.log("RESOLVING")
111
125
  if (!this.layout.metadata)
@@ -8,10 +8,11 @@ import {LayoutBase} from "../../layouts/layoutBase.js"
8
8
  import {LayoutRecord} from "../layoutRecord.js";
9
9
  import {ApiRecordResponseObj} from "../../models/apiResults.js";
10
10
  import {FMError} from "../../FMError.js";
11
+ import {FindRequestSymbol, Query} from "../../utils/query.js";
11
12
 
12
13
  export type SortOrder = "ascend" | "descend"
13
14
  export type FindRequest = {
14
- [key: string]: string,
15
+ [key: string]: Query
15
16
  } & {omit?: "true" | "false"}
16
17
 
17
18
  export type PortalRequest = {
@@ -51,6 +52,17 @@ export class RecordGetOperation<T extends LayoutInterface, OPTIONS extends GetOp
51
52
  return this.queries.length !== 0
52
53
  }
53
54
 
55
+ private formatQueries() {
56
+ return this.queries.map(query => {
57
+ let out: any = {}
58
+ for (let key of Object.keys(query)) {
59
+ if (query[key][FindRequestSymbol]) out[key] = query[key][FindRequestSymbol]
60
+ else {out[key] = query[key]}
61
+ }
62
+ return out
63
+ })
64
+ }
65
+
54
66
  protected generateParamsBody(offset: number, limit: number) {
55
67
  const params: {[key: string]: any} = {
56
68
  limit: limit.toString(),
@@ -68,7 +80,7 @@ export class RecordGetOperation<T extends LayoutInterface, OPTIONS extends GetOp
68
80
  if (this.scriptData.prerequest) params["script.prerequest"] = this.scriptData.prerequest.name
69
81
  if (this.scriptData.prerequest?.parameter) params["script.prerequest.param"] = this.scriptData.prerequest.parameter
70
82
 
71
- if (this.queries.length !== 0) params["query"] = this.queries
83
+ if (this.queries.length !== 0) params["query"] = this.formatQueries()
72
84
 
73
85
  let portals: (keyof typeof this.portals)[] = Object.keys(this.portals)
74
86
  params["portal"] = portals
@@ -140,7 +152,7 @@ export class RecordGetOperation<T extends LayoutInterface, OPTIONS extends GetOp
140
152
  }
141
153
 
142
154
  try {
143
- let res = await this.layout.database.apiRequest<ApiRecordResponseObj>(
155
+ let res = await this.layout.database.apiRequestJSON<ApiRecordResponseObj>(
144
156
  endpoint,
145
157
  reqData
146
158
  )
@@ -41,8 +41,7 @@ export class LayoutRecord extends RecordBase {
41
41
  }
42
42
  if (this.recordId === -1) {
43
43
  // This is a new LayoutRecord
44
- let res = await this.layout.database.apiRequest(`${this.layout.endpoint}/records`, {
45
- port: 443,
44
+ let res = await this.layout.database.apiRequestJSON(`${this.layout.endpoint}/records`, {
46
45
  method: "POST",
47
46
  body: JSON.stringify(data)
48
47
  });
@@ -62,8 +61,7 @@ export class LayoutRecord extends RecordBase {
62
61
  }
63
62
  }
64
63
  // for (let item of Object.keys(data)) extraBody[item] = data[item]
65
- let res = await this.layout.database.apiRequest(this.endpoint, {
66
- port: 443,
64
+ let res = await this.layout.database.apiRequestJSON(this.endpoint, {
67
65
  method: "PATCH",
68
66
  body: JSON.stringify(data)
69
67
  });
@@ -102,8 +100,7 @@ export class LayoutRecord extends RecordBase {
102
100
  }
103
101
  if (!this.layout.metadata)
104
102
  await this.layout.getLayoutMeta();
105
- let res = await this.layout.database.apiRequest(this.endpoint, {
106
- port: 443,
103
+ let res = await this.layout.database.apiRequestJSON(this.endpoint, {
107
104
  method: "GET"
108
105
  });
109
106
  if (res.response && res.messages[0].code === "0") {
@@ -121,8 +118,7 @@ export class LayoutRecord extends RecordBase {
121
118
  }
122
119
  async duplicate() {
123
120
  let trace = new Error();
124
- let res = await this.layout.database.apiRequest(this.endpoint, {
125
- port: 443,
121
+ let res = await this.layout.database.apiRequestJSON(this.endpoint, {
126
122
  method: "POST"
127
123
  });
128
124
  if (!res.response) {
@@ -142,8 +138,7 @@ export class LayoutRecord extends RecordBase {
142
138
  }
143
139
  }
144
140
  async delete() {
145
- let res = await this.layout.database.apiRequest(this.endpoint, {
146
- port: 443,
141
+ let res = await this.layout.database.apiRequestJSON(this.endpoint, {
147
142
  method: "DELETE"
148
143
  });
149
144
  console.log(res);
@@ -57,8 +57,7 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
57
57
 
58
58
  if (this.recordId === -1) {
59
59
  // This is a new LayoutRecord
60
- let res = await this.layout.database.apiRequest<{ recordId: string, modId: string }>(`${this.layout.endpoint}/records`, {
61
- port: 443,
60
+ let res = await this.layout.database.apiRequestJSON<{ recordId: string, modId: string }>(`${this.layout.endpoint}/records`, {
62
61
  method: "POST",
63
62
  body: JSON.stringify(data)
64
63
  })
@@ -80,14 +79,13 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
80
79
  }
81
80
 
82
81
  // for (let item of Object.keys(data)) extraBody[item] = data[item]
83
- let res = await this.layout.database.apiRequest<{
82
+ let res = await this.layout.database.apiRequestJSON<{
84
83
  modId: string, newPortalRecordInfo: {
85
84
  tableName: string,
86
85
  recordId: string,
87
86
  modId: string
88
87
  }[]
89
88
  }>(this.endpoint, {
90
- port: 443,
91
89
  method: "PATCH",
92
90
  body: JSON.stringify(data)
93
91
  })
@@ -128,8 +126,7 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
128
126
  throw "Cannot get this RecordBase until a commit() is done."
129
127
  }
130
128
  if (!this.layout.metadata) await this.layout.getLayoutMeta()
131
- let res = await this.layout.database.apiRequest<ApiRecordResponseObj>(this.endpoint, {
132
- port: 443,
129
+ let res = await this.layout.database.apiRequestJSON<ApiRecordResponseObj>(this.endpoint, {
133
130
  method: "GET"
134
131
  })
135
132
 
@@ -147,8 +144,7 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
147
144
 
148
145
  async duplicate(): Promise<LayoutRecord<LAYOUT>> {
149
146
  let trace = new Error()
150
- let res = await this.layout.database.apiRequest<{recordId: string, modId: string}>(this.endpoint, {
151
- port: 443,
147
+ let res = await this.layout.database.apiRequestJSON<{recordId: string, modId: string}>(this.endpoint, {
152
148
  method: "POST"
153
149
  })
154
150
  if (!res.response) {
@@ -170,8 +166,7 @@ export class LayoutRecord<LAYOUT extends LayoutInterface> extends RecordBase<LAY
170
166
  }
171
167
 
172
168
  async delete(): Promise<void> {
173
- let res = await this.layout.database.apiRequest(this.endpoint, {
174
- port: 443,
169
+ let res = await this.layout.database.apiRequestJSON(this.endpoint, {
175
170
  method: "DELETE"
176
171
  })
177
172
  console.log(res)
@@ -1,4 +1,10 @@
1
+ export declare const FindRequestSymbol: unique symbol;
1
2
  type QueryParameter = string | number | Date;
3
+ export type Query = {
4
+ [FindRequestSymbol]: string;
5
+ };
2
6
  export declare function queryEscape(str: string): string;
3
- export declare function query(strings: TemplateStringsArray, ...args: QueryParameter[]): string;
7
+ export declare function query(strings: TemplateStringsArray, ...args: QueryParameter[]): {
8
+ [FindRequestSymbol]: string;
9
+ };
4
10
  export {};
@@ -1,6 +1,7 @@
1
1
  /*
2
2
  * Copyright (c) 2024. See LICENSE file for more information
3
3
  */
4
+ export const FindRequestSymbol = Symbol("easyfm-findrequest");
4
5
  const SPECIAL_CHARACTERS = ["\\", "=", "<", "≤", "≥", ">", "…", "...", "//", "@", "#", "*", "\"", "~"];
5
6
  export function queryEscape(str) {
6
7
  for (let char of SPECIAL_CHARACTERS) {
@@ -16,5 +17,5 @@ export function query(strings, ...args) {
16
17
  const query = strings.map((str, index) => {
17
18
  return str + (argStrings[index] || '');
18
19
  });
19
- return query.join("");
20
+ return { [FindRequestSymbol]: query.join("") };
20
21
  }
@@ -2,8 +2,10 @@
2
2
  * Copyright (c) 2024. See LICENSE file for more information
3
3
  */
4
4
 
5
+ export const FindRequestSymbol = Symbol("easyfm-findrequest");
5
6
  const SPECIAL_CHARACTERS = ["\\", "=", "<", "≤", "≥", ">", "…", "...", "//", "@", "#", "*", "\"", "~"]
6
7
  type QueryParameter = string | number | Date
8
+ export type Query = {[FindRequestSymbol]: string}
7
9
 
8
10
  export function queryEscape(str: string) {
9
11
  for (let char of SPECIAL_CHARACTERS) {
@@ -21,5 +23,5 @@ export function query(strings: TemplateStringsArray, ...args: QueryParameter[])
21
23
  const query = strings.map((str, index) => {
22
24
  return str + (argStrings[index] || '')
23
25
  })
24
- return query.join("")
26
+ return {[FindRequestSymbol]: query.join("")}
25
27
  }
@@ -1,6 +0,0 @@
1
- import { RecordBase } from "./recordBase.js";
2
- import { FieldBase, FieldValue } from "./fieldBase.js";
3
- export declare abstract class Field<T extends FieldValue> extends FieldBase<T> {
4
- record: RecordBase<any>;
5
- protected constructor(record: RecordBase<any>);
6
- }
@@ -1,11 +0,0 @@
1
- /*
2
- * Copyright (c) 2024. See LICENSE file for more information
3
- */
4
- import { FieldBase } from "./fieldBase.js";
5
- export class Field extends FieldBase {
6
- record;
7
- constructor(record) {
8
- super();
9
- this.record = record;
10
- }
11
- }
@@ -1,4 +0,0 @@
1
- type QueryParameter = string | number | Date;
2
- export declare function fmQueryEscape(str: string): string;
3
- export declare function fmQuery(strings: TemplateStringsArray, ...args: QueryParameter[]): string;
4
- export {};
@@ -1,20 +0,0 @@
1
- /*
2
- * Copyright (c) 2024. See LICENSE file for more information
3
- */
4
- const SPECIAL_CHARACTERS = ["\\", "=", "<", "≤", "≥", ">", "…", "...", "//", "@", "#", "*", "\"", "~"];
5
- export function fmQueryEscape(str) {
6
- for (let char of SPECIAL_CHARACTERS) {
7
- str = str.replace(char, `\\${char}`);
8
- }
9
- return str;
10
- }
11
- export function fmQuery(strings, ...args) {
12
- let argStrings = args.map(item => {
13
- return fmQueryEscape(item.toString());
14
- });
15
- // Zip the parameters together
16
- const query = strings.map((str, index) => {
17
- return str + (args[index] || '');
18
- });
19
- return query.join("");
20
- }