@proofkit/fmdapi 5.1.0-beta.2 → 5.1.0-beta.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/bin/intent.js +11 -11
- package/dist/esm/adapters/{fm-http.d.ts → fm-mcp.d.ts} +4 -4
- package/dist/esm/adapters/{fm-http.js → fm-mcp.js} +7 -7
- package/dist/esm/adapters/fm-mcp.js.map +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/package.json +1 -1
- package/src/adapters/{fm-http.ts → fm-mcp.ts} +8 -8
- package/src/index.ts +1 -1
- package/dist/esm/adapters/fm-http.js.map +0 -1
package/bin/intent.js
CHANGED
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
// Commit this file, then add to your package.json:
|
|
5
5
|
// "bin": { "intent": "./bin/intent.js" }
|
|
6
6
|
try {
|
|
7
|
-
await import(
|
|
7
|
+
await import("@tanstack/intent/intent-library");
|
|
8
8
|
} catch (e) {
|
|
9
|
-
if (e?.code ===
|
|
10
|
-
console.error(
|
|
11
|
-
console.error(
|
|
12
|
-
console.error(
|
|
13
|
-
console.error(
|
|
14
|
-
console.error(
|
|
15
|
-
console.error(
|
|
16
|
-
console.error(
|
|
17
|
-
process.exit(1)
|
|
9
|
+
if (e?.code === "ERR_MODULE_NOT_FOUND" || e?.code === "MODULE_NOT_FOUND") {
|
|
10
|
+
console.error("@tanstack/intent is not installed.");
|
|
11
|
+
console.error("");
|
|
12
|
+
console.error("Install it as a dev dependency:");
|
|
13
|
+
console.error(" npm add -D @tanstack/intent");
|
|
14
|
+
console.error("");
|
|
15
|
+
console.error("Or run directly:");
|
|
16
|
+
console.error(" npx @tanstack/intent@latest list");
|
|
17
|
+
process.exit(1);
|
|
18
18
|
}
|
|
19
|
-
throw e
|
|
19
|
+
throw e;
|
|
20
20
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { CreateResponse, DeleteResponse, GetResponse, LayoutMetadataResponse, ScriptResponse, UpdateResponse } from '../client-types.js';
|
|
2
2
|
import { Adapter, CreateOptions, DeleteOptions, ExecuteScriptOptions, FindOptions, GetOptions, LayoutMetadataOptions, ListOptions, UpdateOptions } from './core.js';
|
|
3
|
-
export interface
|
|
4
|
-
/** Base URL of the local FM
|
|
3
|
+
export interface FmMcpAdapterOptions {
|
|
4
|
+
/** Base URL of the local FM MCP server (e.g. "http://localhost:3000") */
|
|
5
5
|
baseUrl: string;
|
|
6
6
|
/** Name of the connected FileMaker file */
|
|
7
7
|
connectedFileName: string;
|
|
8
8
|
/** Name of the FM script that executes Data API calls. Defaults to "execute_data_api" */
|
|
9
9
|
scriptName?: string;
|
|
10
10
|
}
|
|
11
|
-
export declare class
|
|
11
|
+
export declare class FmMcpAdapter implements Adapter {
|
|
12
12
|
protected baseUrl: string;
|
|
13
13
|
protected connectedFileName: string;
|
|
14
14
|
protected scriptName: string;
|
|
15
|
-
constructor(options:
|
|
15
|
+
constructor(options: FmMcpAdapterOptions);
|
|
16
16
|
protected request: (params: {
|
|
17
17
|
layout: string;
|
|
18
18
|
body: object;
|
|
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { FileMakerError } from "../client-types.js";
|
|
5
5
|
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
6
|
-
class
|
|
6
|
+
class FmMcpAdapter {
|
|
7
7
|
constructor(options) {
|
|
8
8
|
__publicField(this, "baseUrl");
|
|
9
9
|
__publicField(this, "connectedFileName");
|
|
@@ -56,7 +56,7 @@ class FmHttpAdapter {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
if (!res.ok) {
|
|
59
|
-
throw new FileMakerError(String(res.status), `FM
|
|
59
|
+
throw new FileMakerError(String(res.status), `FM MCP request failed (${res.status}): ${await res.text()}`);
|
|
60
60
|
}
|
|
61
61
|
const raw = await res.json();
|
|
62
62
|
let scriptResult;
|
|
@@ -65,7 +65,7 @@ class FmHttpAdapter {
|
|
|
65
65
|
} catch (err) {
|
|
66
66
|
throw new FileMakerError(
|
|
67
67
|
"500",
|
|
68
|
-
`FM
|
|
68
|
+
`FM MCP response parse failed: ${err instanceof Error ? err.message : String(err)}`
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
const respData = scriptResult;
|
|
@@ -149,7 +149,7 @@ class FmHttpAdapter {
|
|
|
149
149
|
})
|
|
150
150
|
});
|
|
151
151
|
if (!res.ok) {
|
|
152
|
-
throw new FileMakerError(String(res.status), `FM
|
|
152
|
+
throw new FileMakerError(String(res.status), `FM MCP executeScript failed (${res.status}): ${await res.text()}`);
|
|
153
153
|
}
|
|
154
154
|
const raw = await res.json();
|
|
155
155
|
return {
|
|
@@ -157,7 +157,7 @@ class FmHttpAdapter {
|
|
|
157
157
|
};
|
|
158
158
|
});
|
|
159
159
|
__publicField(this, "containerUpload", () => {
|
|
160
|
-
throw new Error("Container upload is not supported via FM
|
|
160
|
+
throw new Error("Container upload is not supported via FM MCP adapter");
|
|
161
161
|
});
|
|
162
162
|
this.baseUrl = options.baseUrl.replace(TRAILING_SLASHES_REGEX, "");
|
|
163
163
|
this.connectedFileName = options.connectedFileName;
|
|
@@ -165,6 +165,6 @@ class FmHttpAdapter {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
export {
|
|
168
|
-
|
|
168
|
+
FmMcpAdapter
|
|
169
169
|
};
|
|
170
|
-
//# sourceMappingURL=fm-
|
|
170
|
+
//# sourceMappingURL=fm-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fm-mcp.js","sources":["../../../src/adapters/fm-mcp.ts"],"sourcesContent":["import type {\n CreateResponse,\n DeleteResponse,\n GetResponse,\n LayoutMetadataResponse,\n RawFMResponse,\n ScriptResponse,\n UpdateResponse,\n} from \"../client-types.js\";\nimport { FileMakerError } from \"../client-types.js\";\nimport type {\n Adapter,\n CreateOptions,\n DeleteOptions,\n ExecuteScriptOptions,\n FindOptions,\n GetOptions,\n LayoutMetadataOptions,\n ListOptions,\n UpdateOptions,\n} from \"./core.js\";\n\nconst TRAILING_SLASHES_REGEX = /\\/+$/;\n\nexport interface FmMcpAdapterOptions {\n /** Base URL of the local FM MCP server (e.g. \"http://localhost:3000\") */\n baseUrl: string;\n /** Name of the connected FileMaker file */\n connectedFileName: string;\n /** Name of the FM script that executes Data API calls. Defaults to \"execute_data_api\" */\n scriptName?: string;\n}\n\nexport class FmMcpAdapter implements Adapter {\n protected baseUrl: string;\n protected connectedFileName: string;\n protected scriptName: string;\n\n constructor(options: FmMcpAdapterOptions) {\n this.baseUrl = options.baseUrl.replace(TRAILING_SLASHES_REGEX, \"\");\n this.connectedFileName = options.connectedFileName;\n this.scriptName = options.scriptName ?? \"execute_data_api\";\n }\n\n protected request = async (params: {\n layout: string;\n body: object;\n action?: \"read\" | \"metaData\" | \"create\" | \"update\" | \"delete\";\n timeout?: number;\n fetchOptions?: RequestInit;\n }): Promise<unknown> => {\n const { action = \"read\", layout, body, fetchOptions = {} } = params;\n\n // Normalize underscore-prefixed keys to match FM script expectations\n const normalizedBody: Record<string, unknown> = { ...body } as Record<string, unknown>;\n if (\"_offset\" in normalizedBody) {\n normalizedBody.offset = normalizedBody._offset;\n normalizedBody._offset = undefined;\n }\n if (\"_limit\" in normalizedBody) {\n normalizedBody.limit = normalizedBody._limit;\n normalizedBody._limit = undefined;\n }\n if (\"_sort\" in normalizedBody) {\n normalizedBody.sort = normalizedBody._sort;\n normalizedBody._sort = undefined;\n }\n\n const scriptParam = JSON.stringify({\n ...normalizedBody,\n layouts: layout,\n action,\n version: \"vLatest\",\n });\n\n const controller = new AbortController();\n let timeout: NodeJS.Timeout | null = null;\n if (params.timeout) {\n timeout = setTimeout(() => controller.abort(), params.timeout);\n }\n\n const headers = new Headers(fetchOptions?.headers);\n headers.set(\"Content-Type\", \"application/json\");\n\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/callScript`, {\n ...fetchOptions,\n method: \"POST\",\n headers,\n body: JSON.stringify({\n connectedFileName: this.connectedFileName,\n scriptName: this.scriptName,\n data: scriptParam,\n }),\n signal: controller.signal,\n });\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n }\n\n if (!res.ok) {\n throw new FileMakerError(String(res.status), `FM MCP request failed (${res.status}): ${await res.text()}`);\n }\n\n const raw = await res.json();\n // The /callScript response wraps the script result as a string or object\n let scriptResult: unknown;\n try {\n scriptResult = typeof raw.result === \"string\" ? JSON.parse(raw.result) : (raw.result ?? raw);\n } catch (err) {\n throw new FileMakerError(\n \"500\",\n `FM MCP response parse failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const respData = scriptResult as RawFMResponse;\n\n const errorCode = respData.messages?.[0]?.code;\n if (errorCode !== undefined && errorCode !== \"0\") {\n throw new FileMakerError(\n errorCode,\n `Filemaker Data API failed with (${errorCode}): ${JSON.stringify(respData, null, 2)}`,\n );\n }\n\n return respData.response;\n };\n\n list = async (opts: ListOptions): Promise<GetResponse> => {\n return (await this.request({\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as GetResponse;\n };\n\n get = async (opts: GetOptions): Promise<GetResponse> => {\n return (await this.request({\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as GetResponse;\n };\n\n find = async (opts: FindOptions): Promise<GetResponse> => {\n return (await this.request({\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as GetResponse;\n };\n\n create = async (opts: CreateOptions): Promise<CreateResponse> => {\n return (await this.request({\n action: \"create\",\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as CreateResponse;\n };\n\n update = async (opts: UpdateOptions): Promise<UpdateResponse> => {\n return (await this.request({\n action: \"update\",\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as UpdateResponse;\n };\n\n delete = async (opts: DeleteOptions): Promise<DeleteResponse> => {\n return (await this.request({\n action: \"delete\",\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as DeleteResponse;\n };\n\n layoutMetadata = async (opts: LayoutMetadataOptions): Promise<LayoutMetadataResponse> => {\n return (await this.request({\n action: \"metaData\",\n layout: opts.layout,\n body: {},\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as LayoutMetadataResponse;\n };\n\n executeScript = async (opts: ExecuteScriptOptions): Promise<ScriptResponse> => {\n const res = await fetch(`${this.baseUrl}/callScript`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n connectedFileName: this.connectedFileName,\n scriptName: opts.script,\n data: opts.scriptParam,\n }),\n });\n\n if (!res.ok) {\n throw new FileMakerError(String(res.status), `FM MCP executeScript failed (${res.status}): ${await res.text()}`);\n }\n\n const raw = await res.json();\n return {\n scriptResult: typeof raw.result === \"string\" ? raw.result : JSON.stringify(raw.result),\n } as ScriptResponse;\n };\n\n containerUpload = (): Promise<never> => {\n throw new Error(\"Container upload is not supported via FM MCP adapter\");\n };\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { FetchAdapter } from './adapters/fetch.js';
|
|
2
|
-
export {
|
|
2
|
+
export { FmMcpAdapter, type FmMcpAdapterOptions } from './adapters/fm-mcp.js';
|
|
3
3
|
export { OttoAdapter, type OttoAPIKey } from './adapters/otto.js';
|
|
4
4
|
export { DataApi, DataApi as default } from './client.js';
|
|
5
5
|
export * as clientTypes from './client-types.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FetchAdapter } from "./adapters/fetch.js";
|
|
2
|
-
import {
|
|
2
|
+
import { FmMcpAdapter } from "./adapters/fm-mcp.js";
|
|
3
3
|
import { OttoAdapter } from "./adapters/otto.js";
|
|
4
4
|
import { default as default2, default as default3 } from "./client.js";
|
|
5
5
|
import * as clientTypes from "./client-types.js";
|
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
default2 as DataApi,
|
|
10
10
|
FetchAdapter,
|
|
11
11
|
FileMakerError,
|
|
12
|
-
|
|
12
|
+
FmMcpAdapter,
|
|
13
13
|
OttoAdapter,
|
|
14
14
|
clientTypes,
|
|
15
15
|
default3 as default,
|
package/package.json
CHANGED
|
@@ -22,8 +22,8 @@ import type {
|
|
|
22
22
|
|
|
23
23
|
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
24
24
|
|
|
25
|
-
export interface
|
|
26
|
-
/** Base URL of the local FM
|
|
25
|
+
export interface FmMcpAdapterOptions {
|
|
26
|
+
/** Base URL of the local FM MCP server (e.g. "http://localhost:3000") */
|
|
27
27
|
baseUrl: string;
|
|
28
28
|
/** Name of the connected FileMaker file */
|
|
29
29
|
connectedFileName: string;
|
|
@@ -31,12 +31,12 @@ export interface FmHttpAdapterOptions {
|
|
|
31
31
|
scriptName?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export class
|
|
34
|
+
export class FmMcpAdapter implements Adapter {
|
|
35
35
|
protected baseUrl: string;
|
|
36
36
|
protected connectedFileName: string;
|
|
37
37
|
protected scriptName: string;
|
|
38
38
|
|
|
39
|
-
constructor(options:
|
|
39
|
+
constructor(options: FmMcpAdapterOptions) {
|
|
40
40
|
this.baseUrl = options.baseUrl.replace(TRAILING_SLASHES_REGEX, "");
|
|
41
41
|
this.connectedFileName = options.connectedFileName;
|
|
42
42
|
this.scriptName = options.scriptName ?? "execute_data_api";
|
|
@@ -102,7 +102,7 @@ export class FmHttpAdapter implements Adapter {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (!res.ok) {
|
|
105
|
-
throw new FileMakerError(String(res.status), `FM
|
|
105
|
+
throw new FileMakerError(String(res.status), `FM MCP request failed (${res.status}): ${await res.text()}`);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
const raw = await res.json();
|
|
@@ -113,7 +113,7 @@ export class FmHttpAdapter implements Adapter {
|
|
|
113
113
|
} catch (err) {
|
|
114
114
|
throw new FileMakerError(
|
|
115
115
|
"500",
|
|
116
|
-
`FM
|
|
116
|
+
`FM MCP response parse failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -209,7 +209,7 @@ export class FmHttpAdapter implements Adapter {
|
|
|
209
209
|
});
|
|
210
210
|
|
|
211
211
|
if (!res.ok) {
|
|
212
|
-
throw new FileMakerError(String(res.status), `FM
|
|
212
|
+
throw new FileMakerError(String(res.status), `FM MCP executeScript failed (${res.status}): ${await res.text()}`);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
const raw = await res.json();
|
|
@@ -219,6 +219,6 @@ export class FmHttpAdapter implements Adapter {
|
|
|
219
219
|
};
|
|
220
220
|
|
|
221
221
|
containerUpload = (): Promise<never> => {
|
|
222
|
-
throw new Error("Container upload is not supported via FM
|
|
222
|
+
throw new Error("Container upload is not supported via FM MCP adapter");
|
|
223
223
|
};
|
|
224
224
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { FetchAdapter } from "./adapters/fetch.js";
|
|
2
|
-
export {
|
|
2
|
+
export { FmMcpAdapter, type FmMcpAdapterOptions } from "./adapters/fm-mcp.js";
|
|
3
3
|
export { OttoAdapter, type OttoAPIKey } from "./adapters/otto.js";
|
|
4
4
|
export { DataApi, DataApi as default } from "./client.js";
|
|
5
5
|
export * as clientTypes from "./client-types.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fm-http.js","sources":["../../../src/adapters/fm-http.ts"],"sourcesContent":["import type {\n CreateResponse,\n DeleteResponse,\n GetResponse,\n LayoutMetadataResponse,\n RawFMResponse,\n ScriptResponse,\n UpdateResponse,\n} from \"../client-types.js\";\nimport { FileMakerError } from \"../client-types.js\";\nimport type {\n Adapter,\n CreateOptions,\n DeleteOptions,\n ExecuteScriptOptions,\n FindOptions,\n GetOptions,\n LayoutMetadataOptions,\n ListOptions,\n UpdateOptions,\n} from \"./core.js\";\n\nconst TRAILING_SLASHES_REGEX = /\\/+$/;\n\nexport interface FmHttpAdapterOptions {\n /** Base URL of the local FM HTTP server (e.g. \"http://localhost:3000\") */\n baseUrl: string;\n /** Name of the connected FileMaker file */\n connectedFileName: string;\n /** Name of the FM script that executes Data API calls. Defaults to \"execute_data_api\" */\n scriptName?: string;\n}\n\nexport class FmHttpAdapter implements Adapter {\n protected baseUrl: string;\n protected connectedFileName: string;\n protected scriptName: string;\n\n constructor(options: FmHttpAdapterOptions) {\n this.baseUrl = options.baseUrl.replace(TRAILING_SLASHES_REGEX, \"\");\n this.connectedFileName = options.connectedFileName;\n this.scriptName = options.scriptName ?? \"execute_data_api\";\n }\n\n protected request = async (params: {\n layout: string;\n body: object;\n action?: \"read\" | \"metaData\" | \"create\" | \"update\" | \"delete\";\n timeout?: number;\n fetchOptions?: RequestInit;\n }): Promise<unknown> => {\n const { action = \"read\", layout, body, fetchOptions = {} } = params;\n\n // Normalize underscore-prefixed keys to match FM script expectations\n const normalizedBody: Record<string, unknown> = { ...body } as Record<string, unknown>;\n if (\"_offset\" in normalizedBody) {\n normalizedBody.offset = normalizedBody._offset;\n normalizedBody._offset = undefined;\n }\n if (\"_limit\" in normalizedBody) {\n normalizedBody.limit = normalizedBody._limit;\n normalizedBody._limit = undefined;\n }\n if (\"_sort\" in normalizedBody) {\n normalizedBody.sort = normalizedBody._sort;\n normalizedBody._sort = undefined;\n }\n\n const scriptParam = JSON.stringify({\n ...normalizedBody,\n layouts: layout,\n action,\n version: \"vLatest\",\n });\n\n const controller = new AbortController();\n let timeout: NodeJS.Timeout | null = null;\n if (params.timeout) {\n timeout = setTimeout(() => controller.abort(), params.timeout);\n }\n\n const headers = new Headers(fetchOptions?.headers);\n headers.set(\"Content-Type\", \"application/json\");\n\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/callScript`, {\n ...fetchOptions,\n method: \"POST\",\n headers,\n body: JSON.stringify({\n connectedFileName: this.connectedFileName,\n scriptName: this.scriptName,\n data: scriptParam,\n }),\n signal: controller.signal,\n });\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n }\n\n if (!res.ok) {\n throw new FileMakerError(String(res.status), `FM HTTP request failed (${res.status}): ${await res.text()}`);\n }\n\n const raw = await res.json();\n // The /callScript response wraps the script result as a string or object\n let scriptResult: unknown;\n try {\n scriptResult = typeof raw.result === \"string\" ? JSON.parse(raw.result) : (raw.result ?? raw);\n } catch (err) {\n throw new FileMakerError(\n \"500\",\n `FM HTTP response parse failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const respData = scriptResult as RawFMResponse;\n\n const errorCode = respData.messages?.[0]?.code;\n if (errorCode !== undefined && errorCode !== \"0\") {\n throw new FileMakerError(\n errorCode,\n `Filemaker Data API failed with (${errorCode}): ${JSON.stringify(respData, null, 2)}`,\n );\n }\n\n return respData.response;\n };\n\n list = async (opts: ListOptions): Promise<GetResponse> => {\n return (await this.request({\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as GetResponse;\n };\n\n get = async (opts: GetOptions): Promise<GetResponse> => {\n return (await this.request({\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as GetResponse;\n };\n\n find = async (opts: FindOptions): Promise<GetResponse> => {\n return (await this.request({\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as GetResponse;\n };\n\n create = async (opts: CreateOptions): Promise<CreateResponse> => {\n return (await this.request({\n action: \"create\",\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as CreateResponse;\n };\n\n update = async (opts: UpdateOptions): Promise<UpdateResponse> => {\n return (await this.request({\n action: \"update\",\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as UpdateResponse;\n };\n\n delete = async (opts: DeleteOptions): Promise<DeleteResponse> => {\n return (await this.request({\n action: \"delete\",\n body: opts.data,\n layout: opts.layout,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as DeleteResponse;\n };\n\n layoutMetadata = async (opts: LayoutMetadataOptions): Promise<LayoutMetadataResponse> => {\n return (await this.request({\n action: \"metaData\",\n layout: opts.layout,\n body: {},\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n })) as LayoutMetadataResponse;\n };\n\n executeScript = async (opts: ExecuteScriptOptions): Promise<ScriptResponse> => {\n const res = await fetch(`${this.baseUrl}/callScript`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n connectedFileName: this.connectedFileName,\n scriptName: opts.script,\n data: opts.scriptParam,\n }),\n });\n\n if (!res.ok) {\n throw new FileMakerError(String(res.status), `FM HTTP executeScript failed (${res.status}): ${await res.text()}`);\n }\n\n const raw = await res.json();\n return {\n scriptResult: typeof raw.result === \"string\" ? raw.result : JSON.stringify(raw.result),\n } as ScriptResponse;\n };\n\n containerUpload = (): Promise<never> => {\n throw new Error(\"Container upload is not supported via FM HTTP adapter\");\n };\n}\n"],"names":[],"mappings":"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"}
|