@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 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('@tanstack/intent/intent-library')
7
+ await import("@tanstack/intent/intent-library");
8
8
  } catch (e) {
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)
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 FmHttpAdapterOptions {
4
- /** Base URL of the local FM HTTP server (e.g. "http://localhost:3000") */
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 FmHttpAdapter implements Adapter {
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: FmHttpAdapterOptions);
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 FmHttpAdapter {
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 HTTP request failed (${res.status}): ${await res.text()}`);
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 HTTP response parse failed: ${err instanceof Error ? err.message : String(err)}`
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 HTTP executeScript failed (${res.status}): ${await res.text()}`);
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 HTTP adapter");
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
- FmHttpAdapter
168
+ FmMcpAdapter
169
169
  };
170
- //# sourceMappingURL=fm-http.js.map
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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"}
@@ -1,5 +1,5 @@
1
1
  export { FetchAdapter } from './adapters/fetch.js';
2
- export { FmHttpAdapter, type FmHttpAdapterOptions } from './adapters/fm-http.js';
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 { FmHttpAdapter } from "./adapters/fm-http.js";
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
- FmHttpAdapter,
12
+ FmMcpAdapter,
13
13
  OttoAdapter,
14
14
  clientTypes,
15
15
  default3 as default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofkit/fmdapi",
3
- "version": "5.1.0-beta.2",
3
+ "version": "5.1.0-beta.3",
4
4
  "description": "FileMaker Data API client",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,8 +22,8 @@ import type {
22
22
 
23
23
  const TRAILING_SLASHES_REGEX = /\/+$/;
24
24
 
25
- export interface FmHttpAdapterOptions {
26
- /** Base URL of the local FM HTTP server (e.g. "http://localhost:3000") */
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 FmHttpAdapter implements Adapter {
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: FmHttpAdapterOptions) {
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 HTTP request failed (${res.status}): ${await res.text()}`);
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 HTTP response parse failed: ${err instanceof Error ? err.message : String(err)}`,
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 HTTP executeScript failed (${res.status}): ${await res.text()}`);
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 HTTP adapter");
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 { FmHttpAdapter, type FmHttpAdapterOptions } from "./adapters/fm-http.js";
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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"}