@proofkit/fmdapi 5.0.3-beta.0 → 5.1.0-beta.2

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.
Files changed (47) hide show
  1. package/bin/intent.js +20 -0
  2. package/dist/esm/adapters/core.d.ts +4 -4
  3. package/dist/esm/adapters/fetch-base-types.d.ts +4 -4
  4. package/dist/esm/adapters/fetch-base.d.ts +2 -2
  5. package/dist/esm/adapters/fetch-base.js +36 -49
  6. package/dist/esm/adapters/fetch-base.js.map +1 -1
  7. package/dist/esm/adapters/fetch.d.ts +5 -5
  8. package/dist/esm/adapters/fetch.js +11 -10
  9. package/dist/esm/adapters/fetch.js.map +1 -1
  10. package/dist/esm/adapters/fm-http.d.ts +32 -0
  11. package/dist/esm/adapters/fm-http.js +170 -0
  12. package/dist/esm/adapters/fm-http.js.map +1 -0
  13. package/dist/esm/adapters/otto.d.ts +2 -2
  14. package/dist/esm/adapters/otto.js +3 -5
  15. package/dist/esm/adapters/otto.js.map +1 -1
  16. package/dist/esm/client-types.d.ts +41 -41
  17. package/dist/esm/client-types.js +1 -6
  18. package/dist/esm/client-types.js.map +1 -1
  19. package/dist/esm/client.d.ts +28 -44
  20. package/dist/esm/client.js +75 -80
  21. package/dist/esm/client.js.map +1 -1
  22. package/dist/esm/index.d.ts +5 -6
  23. package/dist/esm/index.js +7 -5
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/tokenStore/index.d.ts +1 -1
  26. package/dist/esm/tokenStore/memory.js.map +1 -1
  27. package/dist/esm/tokenStore/types.d.ts +2 -2
  28. package/dist/esm/tokenStore/upstash.d.ts +1 -1
  29. package/dist/esm/utils.d.ts +7 -7
  30. package/dist/esm/utils.js +6 -4
  31. package/dist/esm/utils.js.map +1 -1
  32. package/package.json +37 -26
  33. package/skills/fmdapi-client/SKILL.md +490 -0
  34. package/src/adapters/core.ts +6 -9
  35. package/src/adapters/fetch-base-types.ts +5 -3
  36. package/src/adapters/fetch-base.ts +53 -78
  37. package/src/adapters/fetch.ts +19 -24
  38. package/src/adapters/fm-http.ts +224 -0
  39. package/src/adapters/otto.ts +8 -8
  40. package/src/client-types.ts +59 -83
  41. package/src/client.ts +131 -167
  42. package/src/index.ts +5 -9
  43. package/src/tokenStore/file.ts +2 -4
  44. package/src/tokenStore/index.ts +1 -1
  45. package/src/tokenStore/types.ts +2 -2
  46. package/src/tokenStore/upstash.ts +2 -5
  47. package/src/utils.ts +16 -23
package/bin/intent.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ // Auto-generated by @tanstack/intent setup
3
+ // Exposes the intent end-user CLI for consumers of this library.
4
+ // Commit this file, then add to your package.json:
5
+ // "bin": { "intent": "./bin/intent.js" }
6
+ try {
7
+ await import('@tanstack/intent/intent-library')
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)
18
+ }
19
+ throw e
20
+ }
@@ -1,9 +1,9 @@
1
- import { CreateParams, CreateResponse, DeleteParams, DeleteResponse, FieldData, GetParams, GetResponse, ListParamsRaw, LayoutMetadataResponse, Query, UpdateParams, UpdateResponse, ScriptResponse } from '../client-types.js';
2
- export type BaseRequest = {
1
+ import { CreateParams, CreateResponse, DeleteParams, DeleteResponse, FieldData, GetParams, GetResponse, LayoutMetadataResponse, ListParamsRaw, Query, ScriptResponse, UpdateParams, UpdateResponse } from '../client-types.js';
2
+ export interface BaseRequest {
3
3
  layout: string;
4
4
  fetch?: RequestInit;
5
5
  timeout?: number;
6
- };
6
+ }
7
7
  export type ListOptions = BaseRequest & {
8
8
  data: ListParamsRaw;
9
9
  };
@@ -14,7 +14,7 @@ export type GetOptions = BaseRequest & {
14
14
  };
15
15
  export type FindOptions = BaseRequest & {
16
16
  data: ListParamsRaw & {
17
- query: Array<Query>;
17
+ query: Query[];
18
18
  };
19
19
  };
20
20
  export type CreateOptions = BaseRequest & {
@@ -1,7 +1,7 @@
1
- export type BaseFetchAdapterOptions = {
1
+ export interface BaseFetchAdapterOptions {
2
2
  server: string;
3
3
  db: string;
4
- };
5
- export type GetTokenArguments = {
4
+ }
5
+ export interface GetTokenArguments {
6
6
  refresh?: boolean;
7
- };
7
+ }
@@ -4,12 +4,12 @@ import { BaseFetchAdapterOptions, GetTokenArguments } from './fetch-base-types.j
4
4
  export declare class BaseFetchAdapter implements Adapter {
5
5
  protected server: string;
6
6
  protected db: string;
7
- private refreshToken;
7
+ private readonly refreshToken;
8
8
  baseUrl: URL;
9
9
  constructor(options: BaseFetchAdapterOptions & {
10
10
  refreshToken?: boolean;
11
11
  });
12
- protected getToken: (args?: GetTokenArguments) => Promise<string>;
12
+ protected getToken: (_args?: GetTokenArguments) => Promise<string>;
13
13
  protected request: (params: {
14
14
  url: string;
15
15
  body?: object | FormData;
@@ -8,33 +8,22 @@ class BaseFetchAdapter {
8
8
  __publicField(this, "db");
9
9
  __publicField(this, "refreshToken");
10
10
  __publicField(this, "baseUrl");
11
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
- __publicField(this, "getToken", async (args) => {
13
- throw new Error("getToken method not implemented by Fetch Adapter");
11
+ __publicField(this, "getToken", (_args) => {
12
+ return Promise.reject(new Error("getToken method not implemented by Fetch Adapter"));
14
13
  });
15
14
  __publicField(this, "request", async (params) => {
16
15
  var _a, _b;
17
- const {
18
- query,
19
- body,
20
- method = "GET",
21
- retry = false,
22
- fetchOptions = {}
23
- } = params;
16
+ const { query, body, method = "GET", retry = false, fetchOptions = {} } = params;
24
17
  const url = new URL(`${this.baseUrl}${params.url}`);
25
18
  if (query) {
26
19
  const { _sort, ...rest } = query;
27
- const searchParams = new URLSearchParams(rest);
20
+ const filteredRest = Object.fromEntries(Object.entries(rest).filter(([, v]) => v !== void 0 && v !== null));
21
+ const searchParams = new URLSearchParams(filteredRest);
28
22
  if (query.portalRanges && typeof query.portalRanges === "object") {
29
- for (const [portalName, value] of Object.entries(
30
- query.portalRanges
31
- )) {
23
+ for (const [portalName, value] of Object.entries(query.portalRanges)) {
32
24
  if (value) {
33
25
  if (value.offset && value.offset > 0) {
34
- searchParams.set(
35
- `_offset.${portalName}`,
36
- value.offset.toString()
37
- );
26
+ searchParams.set(`_offset.${portalName}`, value.offset.toString());
38
27
  }
39
28
  if (value.limit) {
40
29
  searchParams.set(`_limit.${portalName}`, value.limit.toString());
@@ -49,46 +38,47 @@ class BaseFetchAdapter {
49
38
  url.search = searchParams.toString();
50
39
  }
51
40
  if (body && "portalRanges" in body) {
52
- for (const [portalName, value] of Object.entries(
53
- body.portalRanges
54
- )) {
41
+ for (const [portalName, value] of Object.entries(body.portalRanges)) {
55
42
  if (value) {
56
43
  if (value.offset && value.offset > 0) {
57
- url.searchParams.set(
58
- `_offset.${portalName}`,
59
- value.offset.toString()
60
- );
44
+ url.searchParams.set(`_offset.${portalName}`, value.offset.toString());
61
45
  }
62
46
  if (value.limit) {
63
- url.searchParams.set(
64
- `_limit.${portalName}`,
65
- value.limit.toString()
66
- );
47
+ url.searchParams.set(`_limit.${portalName}`, value.limit.toString());
67
48
  }
68
49
  }
69
50
  }
70
- delete body.portalRanges;
51
+ body.portalRanges = void 0;
71
52
  }
72
53
  const controller = new AbortController();
73
54
  let timeout = null;
74
- if (params.timeout)
55
+ if (params.timeout) {
75
56
  timeout = setTimeout(() => controller.abort(), params.timeout);
57
+ }
76
58
  const token = await this.getToken({ refresh: retry });
77
59
  const headers = new Headers(fetchOptions == null ? void 0 : fetchOptions.headers);
78
60
  headers.set("Authorization", `Bearer ${token}`);
79
61
  if (!(body instanceof FormData)) {
80
62
  headers.set("Content-Type", "application/json");
81
63
  }
64
+ let requestBody;
65
+ if (body instanceof FormData) {
66
+ requestBody = body;
67
+ } else if (body) {
68
+ requestBody = JSON.stringify(body);
69
+ } else {
70
+ requestBody = void 0;
71
+ }
82
72
  const res = await fetch(url.toString(), {
83
73
  ...fetchOptions,
84
74
  method,
85
- body: body instanceof FormData ? body : body ? JSON.stringify(body) : void 0,
75
+ body: requestBody,
86
76
  headers,
87
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
88
- // @ts-ignore
89
77
  signal: controller.signal
90
78
  });
91
- if (timeout) clearTimeout(timeout);
79
+ if (timeout) {
80
+ clearTimeout(timeout);
81
+ }
92
82
  let respData;
93
83
  try {
94
84
  respData = await res.json();
@@ -98,16 +88,11 @@ class BaseFetchAdapter {
98
88
  if (!res.ok) {
99
89
  if (((_a = respData == null ? void 0 : respData.messages) == null ? void 0 : _a[0].code) === "952" && !retry && this.refreshToken) {
100
90
  return this.request({ ...params, retry: true });
101
- } else {
102
- throw new FileMakerError(
103
- ((_b = respData == null ? void 0 : respData.messages) == null ? void 0 : _b[0].code) ?? "500",
104
- `Filemaker Data API failed with (${res.status}): ${JSON.stringify(
105
- respData,
106
- null,
107
- 2
108
- )}`
109
- );
110
91
  }
92
+ throw new FileMakerError(
93
+ ((_b = respData == null ? void 0 : respData.messages) == null ? void 0 : _b[0].code) ?? "500",
94
+ `Filemaker Data API failed with (${res.status}): ${JSON.stringify(respData, null, 2)}`
95
+ );
111
96
  }
112
97
  return respData.response;
113
98
  });
@@ -218,7 +203,9 @@ class BaseFetchAdapter {
218
203
  });
219
204
  __publicField(this, "containerUpload", async (opts) => {
220
205
  let url = `/layouts/${opts.layout}/records/${opts.data.recordId}/containers/${opts.data.containerFieldName}`;
221
- if (opts.data.repetition) url += `/${opts.data.repetition}`;
206
+ if (opts.data.repetition) {
207
+ url += `/${opts.data.repetition}`;
208
+ }
222
209
  const formData = new FormData();
223
210
  formData.append("upload", opts.data.file);
224
211
  await this.request({
@@ -244,10 +231,10 @@ class BaseFetchAdapter {
244
231
  this.server = options.server;
245
232
  this.db = options.db;
246
233
  this.refreshToken = options.refreshToken ?? false;
247
- this.baseUrl = new URL(
248
- `${this.server}/fmi/data/vLatest/databases/${this.db}`
249
- );
250
- if (this.db === "") throw new Error("Database name is required");
234
+ this.baseUrl = new URL(`${this.server}/fmi/data/vLatest/databases/${this.db}`);
235
+ if (this.db === "") {
236
+ throw new Error("Database name is required");
237
+ }
251
238
  }
252
239
  }
253
240
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-base.js","sources":["../../../src/adapters/fetch-base.ts"],"sourcesContent":["import type {\n AllLayoutsMetadataResponse,\n CreateResponse,\n DeleteResponse,\n GetResponse,\n LayoutMetadataResponse,\n PortalRanges,\n RawFMResponse,\n ScriptResponse,\n ScriptsMetadataResponse,\n UpdateResponse,\n} from \"../client-types.js\";\nimport { FileMakerError } from \"../client-types.js\";\nimport type {\n Adapter,\n BaseRequest,\n ContainerUploadOptions,\n CreateOptions,\n DeleteOptions,\n ExecuteScriptOptions,\n FindOptions,\n GetOptions,\n LayoutMetadataOptions,\n ListOptions,\n UpdateOptions,\n} from \"./core.js\";\nimport type {\n BaseFetchAdapterOptions,\n GetTokenArguments,\n} from \"./fetch-base-types.js\";\n\nexport class BaseFetchAdapter implements Adapter {\n protected server: string;\n protected db: string;\n private refreshToken: boolean;\n baseUrl: URL;\n\n constructor(options: BaseFetchAdapterOptions & { refreshToken?: boolean }) {\n this.server = options.server;\n this.db = options.db;\n this.refreshToken = options.refreshToken ?? false;\n this.baseUrl = new URL(\n `${this.server}/fmi/data/vLatest/databases/${this.db}`,\n );\n\n if (this.db === \"\") throw new Error(\"Database name is required\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected getToken = async (args?: GetTokenArguments): Promise<string> => {\n // method must be implemented in subclass\n throw new Error(\"getToken method not implemented by Fetch Adapter\");\n };\n\n protected request = async (params: {\n url: string;\n body?: object | FormData;\n query?: Record<string, string>;\n method?: string;\n retry?: boolean;\n portalRanges?: PortalRanges;\n timeout?: number;\n fetchOptions?: RequestInit;\n }): Promise<unknown> => {\n const {\n query,\n body,\n method = \"GET\",\n retry = false,\n fetchOptions = {},\n } = params;\n\n const url = new URL(`${this.baseUrl}${params.url}`);\n\n if (query) {\n const { _sort, ...rest } = query;\n const searchParams = new URLSearchParams(rest);\n if (query.portalRanges && typeof query.portalRanges === \"object\") {\n for (const [portalName, value] of Object.entries(\n query.portalRanges as PortalRanges,\n )) {\n if (value) {\n if (value.offset && value.offset > 0) {\n searchParams.set(\n `_offset.${portalName}`,\n value.offset.toString(),\n );\n }\n if (value.limit) {\n searchParams.set(`_limit.${portalName}`, value.limit.toString());\n }\n }\n }\n }\n if (_sort) {\n searchParams.set(\"_sort\", JSON.stringify(_sort));\n }\n searchParams.delete(\"portalRanges\");\n url.search = searchParams.toString();\n }\n\n if (body && \"portalRanges\" in body) {\n for (const [portalName, value] of Object.entries(\n body.portalRanges as PortalRanges,\n )) {\n if (value) {\n if (value.offset && value.offset > 0) {\n url.searchParams.set(\n `_offset.${portalName}`,\n value.offset.toString(),\n );\n }\n if (value.limit) {\n url.searchParams.set(\n `_limit.${portalName}`,\n value.limit.toString(),\n );\n }\n }\n }\n delete body.portalRanges;\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 const token = await this.getToken({ refresh: retry });\n\n const headers = new Headers(fetchOptions?.headers);\n headers.set(\"Authorization\", `Bearer ${token}`);\n\n // Only set Content-Type for JSON bodies\n if (!(body instanceof FormData)) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n const res = await fetch(url.toString(), {\n ...fetchOptions,\n method,\n body:\n body instanceof FormData\n ? body\n : body\n ? JSON.stringify(body)\n : undefined,\n headers,\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n signal: controller.signal,\n });\n\n if (timeout) clearTimeout(timeout);\n\n let respData: RawFMResponse;\n try {\n respData = await res.json();\n } catch {\n respData = {};\n }\n\n if (!res.ok) {\n if (\n respData?.messages?.[0].code === \"952\" &&\n !retry &&\n this.refreshToken\n ) {\n // token expired, get new token and retry once\n return this.request({ ...params, retry: true });\n } else {\n throw new FileMakerError(\n respData?.messages?.[0].code ?? \"500\",\n `Filemaker Data API failed with (${res.status}): ${JSON.stringify(\n respData,\n null,\n 2,\n )}`,\n );\n }\n }\n\n return respData.response;\n };\n\n public list = async (opts: ListOptions): Promise<GetResponse> => {\n const { data, layout } = opts;\n\n const resp = await this.request({\n url: `/layouts/${layout}/records`,\n query: data as Record<string, string>,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as GetResponse;\n };\n\n public get = async (opts: GetOptions): Promise<GetResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records/${data.recordId}`,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as GetResponse;\n };\n\n public find = async (opts: FindOptions): Promise<GetResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/_find`,\n body: data,\n method: \"POST\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as GetResponse;\n };\n\n public create = async (opts: CreateOptions): Promise<CreateResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records`,\n body: data,\n method: \"POST\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as CreateResponse;\n };\n\n public update = async (opts: UpdateOptions): Promise<UpdateResponse> => {\n const {\n data: { recordId, ...data },\n layout,\n } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records/${recordId}`,\n body: data,\n method: \"PATCH\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as UpdateResponse;\n };\n\n public delete = async (opts: DeleteOptions): Promise<DeleteResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records/${data.recordId}`,\n method: \"DELETE\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as DeleteResponse;\n };\n\n public layoutMetadata = async (\n opts: LayoutMetadataOptions,\n ): Promise<LayoutMetadataResponse> => {\n return (await this.request({\n url: `/layouts/${opts.layout}`,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n })) as LayoutMetadataResponse;\n };\n\n /**\n * Execute a script within the database\n */\n public executeScript = async (opts: ExecuteScriptOptions) => {\n const { script, scriptParam, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/script/${script}`,\n query: scriptParam ? { \"script.param\": scriptParam } : undefined,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as ScriptResponse;\n };\n\n /**\n * Returns a list of available layouts on the database.\n */\n public layouts = async (opts?: Omit<BaseRequest, \"layout\">) => {\n return (await this.request({\n url: \"/layouts\",\n fetchOptions: opts?.fetch,\n timeout: opts?.timeout,\n })) as AllLayoutsMetadataResponse;\n };\n\n /**\n * Returns a list of available scripts on the database.\n */\n public scripts = async (opts?: Omit<BaseRequest, \"layout\">) => {\n return (await this.request({\n url: \"/scripts\",\n fetchOptions: opts?.fetch,\n timeout: opts?.timeout,\n })) as ScriptsMetadataResponse;\n };\n\n public containerUpload = async (opts: ContainerUploadOptions) => {\n let url = `/layouts/${opts.layout}/records/${opts.data.recordId}/containers/${opts.data.containerFieldName}`;\n if (opts.data.repetition) url += `/${opts.data.repetition}`;\n const formData = new FormData();\n formData.append(\"upload\", opts.data.file);\n\n await this.request({\n url,\n method: \"POST\",\n body: formData,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n });\n };\n\n /**\n * Set global fields for the current session\n */\n public globals = async (\n opts: Omit<BaseRequest, \"layout\"> & {\n globalFields: Record<string, string | number>;\n },\n ) => {\n return (await this.request({\n url: \"/globals\",\n method: \"PATCH\",\n body: { globalFields: opts.globalFields },\n fetchOptions: opts?.fetch,\n timeout: opts?.timeout,\n })) as Record<string, never>;\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch-base.js","sources":["../../../src/adapters/fetch-base.ts"],"sourcesContent":["import type {\n AllLayoutsMetadataResponse,\n CreateResponse,\n DeleteResponse,\n GetResponse,\n LayoutMetadataResponse,\n PortalRanges,\n RawFMResponse,\n ScriptResponse,\n ScriptsMetadataResponse,\n UpdateResponse,\n} from \"../client-types.js\";\nimport { FileMakerError } from \"../client-types.js\";\nimport type {\n Adapter,\n BaseRequest,\n ContainerUploadOptions,\n CreateOptions,\n DeleteOptions,\n ExecuteScriptOptions,\n FindOptions,\n GetOptions,\n LayoutMetadataOptions,\n ListOptions,\n UpdateOptions,\n} from \"./core.js\";\nimport type { BaseFetchAdapterOptions, GetTokenArguments } from \"./fetch-base-types.js\";\n\nexport class BaseFetchAdapter implements Adapter {\n protected server: string;\n protected db: string;\n private readonly refreshToken: boolean;\n baseUrl: URL;\n\n constructor(options: BaseFetchAdapterOptions & { refreshToken?: boolean }) {\n this.server = options.server;\n this.db = options.db;\n this.refreshToken = options.refreshToken ?? false;\n this.baseUrl = new URL(`${this.server}/fmi/data/vLatest/databases/${this.db}`);\n\n if (this.db === \"\") {\n throw new Error(\"Database name is required\");\n }\n }\n\n protected getToken = (_args?: GetTokenArguments): Promise<string> => {\n // method must be implemented in subclass\n return Promise.reject(new Error(\"getToken method not implemented by Fetch Adapter\"));\n };\n\n protected request = async (params: {\n url: string;\n body?: object | FormData;\n query?: Record<string, string>;\n method?: string;\n retry?: boolean;\n portalRanges?: PortalRanges;\n timeout?: number;\n fetchOptions?: RequestInit;\n }): Promise<unknown> => {\n const { query, body, method = \"GET\", retry = false, fetchOptions = {} } = params;\n\n const url = new URL(`${this.baseUrl}${params.url}`);\n\n if (query) {\n const { _sort, ...rest } = query;\n // Filter out undefined/null values before creating URLSearchParams\n const filteredRest = Object.fromEntries(Object.entries(rest).filter(([, v]) => v !== undefined && v !== null));\n const searchParams = new URLSearchParams(filteredRest as Record<string, string>);\n if (query.portalRanges && typeof query.portalRanges === \"object\") {\n for (const [portalName, value] of Object.entries(query.portalRanges as PortalRanges)) {\n if (value) {\n if (value.offset && value.offset > 0) {\n searchParams.set(`_offset.${portalName}`, value.offset.toString());\n }\n if (value.limit) {\n searchParams.set(`_limit.${portalName}`, value.limit.toString());\n }\n }\n }\n }\n if (_sort) {\n searchParams.set(\"_sort\", JSON.stringify(_sort));\n }\n searchParams.delete(\"portalRanges\");\n url.search = searchParams.toString();\n }\n\n if (body && \"portalRanges\" in body) {\n for (const [portalName, value] of Object.entries(body.portalRanges as PortalRanges)) {\n if (value) {\n if (value.offset && value.offset > 0) {\n url.searchParams.set(`_offset.${portalName}`, value.offset.toString());\n }\n if (value.limit) {\n url.searchParams.set(`_limit.${portalName}`, value.limit.toString());\n }\n }\n }\n body.portalRanges = undefined;\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 token = await this.getToken({ refresh: retry });\n\n const headers = new Headers(fetchOptions?.headers);\n headers.set(\"Authorization\", `Bearer ${token}`);\n\n // Only set Content-Type for JSON bodies\n if (!(body instanceof FormData)) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n let requestBody: string | FormData | undefined;\n if (body instanceof FormData) {\n requestBody = body;\n } else if (body) {\n requestBody = JSON.stringify(body);\n } else {\n requestBody = undefined;\n }\n\n const res = await fetch(url.toString(), {\n ...fetchOptions,\n method,\n body: requestBody,\n headers,\n signal: controller.signal,\n });\n\n if (timeout) {\n clearTimeout(timeout);\n }\n\n let respData: RawFMResponse;\n try {\n respData = await res.json();\n } catch {\n respData = {};\n }\n\n if (!res.ok) {\n if (respData?.messages?.[0].code === \"952\" && !retry && this.refreshToken) {\n // token expired, get new token and retry once\n return this.request({ ...params, retry: true });\n }\n throw new FileMakerError(\n respData?.messages?.[0].code ?? \"500\",\n `Filemaker Data API failed with (${res.status}): ${JSON.stringify(respData, null, 2)}`,\n );\n }\n\n return respData.response;\n };\n\n list = async (opts: ListOptions): Promise<GetResponse> => {\n const { data, layout } = opts;\n\n const resp = await this.request({\n url: `/layouts/${layout}/records`,\n query: data as Record<string, string>,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as GetResponse;\n };\n\n get = async (opts: GetOptions): Promise<GetResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records/${data.recordId}`,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as GetResponse;\n };\n\n find = async (opts: FindOptions): Promise<GetResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/_find`,\n body: data,\n method: \"POST\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as GetResponse;\n };\n\n create = async (opts: CreateOptions): Promise<CreateResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records`,\n body: data,\n method: \"POST\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as CreateResponse;\n };\n\n update = async (opts: UpdateOptions): Promise<UpdateResponse> => {\n const {\n data: { recordId, ...data },\n layout,\n } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records/${recordId}`,\n body: data,\n method: \"PATCH\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as UpdateResponse;\n };\n\n delete = async (opts: DeleteOptions): Promise<DeleteResponse> => {\n const { data, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/records/${data.recordId}`,\n method: \"DELETE\",\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as DeleteResponse;\n };\n\n layoutMetadata = async (opts: LayoutMetadataOptions): Promise<LayoutMetadataResponse> => {\n return (await this.request({\n url: `/layouts/${opts.layout}`,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n })) as LayoutMetadataResponse;\n };\n\n /**\n * Execute a script within the database\n */\n executeScript = async (opts: ExecuteScriptOptions) => {\n const { script, scriptParam, layout } = opts;\n const resp = await this.request({\n url: `/layouts/${layout}/script/${script}`,\n query: scriptParam ? { \"script.param\": scriptParam } : undefined,\n fetchOptions: opts.fetch,\n timeout: opts.timeout,\n });\n return resp as ScriptResponse;\n };\n\n /**\n * Returns a list of available layouts on the database.\n */\n layouts = async (opts?: Omit<BaseRequest, \"layout\">) => {\n return (await this.request({\n url: \"/layouts\",\n fetchOptions: opts?.fetch,\n timeout: opts?.timeout,\n })) as AllLayoutsMetadataResponse;\n };\n\n /**\n * Returns a list of available scripts on the database.\n */\n scripts = async (opts?: Omit<BaseRequest, \"layout\">) => {\n return (await this.request({\n url: \"/scripts\",\n fetchOptions: opts?.fetch,\n timeout: opts?.timeout,\n })) as ScriptsMetadataResponse;\n };\n\n containerUpload = async (opts: ContainerUploadOptions) => {\n let url = `/layouts/${opts.layout}/records/${opts.data.recordId}/containers/${opts.data.containerFieldName}`;\n if (opts.data.repetition) {\n url += `/${opts.data.repetition}`;\n }\n const formData = new FormData();\n formData.append(\"upload\", opts.data.file);\n\n await this.request({\n url,\n method: \"POST\",\n body: formData,\n timeout: opts.timeout,\n fetchOptions: opts.fetch,\n });\n };\n\n /**\n * Set global fields for the current session\n */\n globals = async (\n opts: Omit<BaseRequest, \"layout\"> & {\n globalFields: Record<string, string | number>;\n },\n ) => {\n return (await this.request({\n url: \"/globals\",\n method: \"PATCH\",\n body: { globalFields: opts.globalFields },\n fetchOptions: opts?.fetch,\n timeout: opts?.timeout,\n })) as Record<string, never>;\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { TokenStoreDefinitions } from '../tokenStore/types.js';
2
- import { BaseFetchAdapterOptions, GetTokenArguments } from './fetch-base-types.js';
3
2
  import { BaseFetchAdapter } from './fetch-base.js';
3
+ import { BaseFetchAdapterOptions, GetTokenArguments } from './fetch-base-types.js';
4
4
  export interface FetchAdapterOptions extends BaseFetchAdapterOptions {
5
5
  auth: {
6
6
  username: string;
@@ -9,10 +9,10 @@ export interface FetchAdapterOptions extends BaseFetchAdapterOptions {
9
9
  tokenStore?: TokenStoreDefinitions;
10
10
  }
11
11
  export declare class FetchAdapter extends BaseFetchAdapter {
12
- private username;
13
- private password;
14
- private tokenStore;
15
- private getTokenKey;
12
+ private readonly username;
13
+ private readonly password;
14
+ private readonly tokenStore;
15
+ private readonly getTokenKey;
16
16
  constructor(args: FetchAdapterOptions);
17
17
  /**
18
18
  * Gets a FileMaker Data API token for authentication.
@@ -31,20 +31,17 @@ class FetchAdapter extends BaseFetchAdapter {
31
31
  method: "POST",
32
32
  headers: {
33
33
  "Content-Type": "application/json",
34
- Authorization: `Basic ${Buffer.from(
35
- `${this.username}:${this.password}`
36
- ).toString("base64")}`
34
+ Authorization: `Basic ${Buffer.from(`${this.username}:${this.password}`).toString("base64")}`
37
35
  }
38
36
  });
39
37
  if (!res.ok) {
40
38
  const data = await res.json();
41
- throw new FileMakerError(
42
- data.messages[0].code,
43
- data.messages[0].message
44
- );
39
+ throw new FileMakerError(data.messages[0].code, data.messages[0].message);
45
40
  }
46
41
  token = res.headers.get("X-FM-Data-Access-Token");
47
- if (!token) throw new Error("Could not get token");
42
+ if (!token) {
43
+ throw new Error("Could not get token");
44
+ }
48
45
  this.tokenStore.setToken(this.getTokenKey(), token);
49
46
  }
50
47
  return token;
@@ -69,8 +66,12 @@ class FetchAdapter extends BaseFetchAdapter {
69
66
  this.password = args.auth.password;
70
67
  this.tokenStore = args.tokenStore ?? memoryStore();
71
68
  this.getTokenKey = ((_a = args.tokenStore) == null ? void 0 : _a.getKey) ?? (() => `${args.server}/${args.db}`);
72
- if (this.username === "") throw new Error("Username is required");
73
- if (this.password === "") throw new Error("Password is required");
69
+ if (this.username === "") {
70
+ throw new Error("Username is required");
71
+ }
72
+ if (this.password === "") {
73
+ throw new Error("Password is required");
74
+ }
74
75
  }
75
76
  }
76
77
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.js","sources":["../../../src/adapters/fetch.ts"],"sourcesContent":["import { FileMakerError } from \"../client-types.js\";\nimport memoryStore from \"../tokenStore/memory.js\";\nimport type { TokenStoreDefinitions } from \"../tokenStore/types.js\";\nimport type {\n BaseFetchAdapterOptions,\n GetTokenArguments,\n} from \"./fetch-base-types.js\";\nimport { BaseFetchAdapter } from \"./fetch-base.js\";\n\nexport interface FetchAdapterOptions extends BaseFetchAdapterOptions {\n auth: {\n username: string;\n password: string;\n };\n tokenStore?: TokenStoreDefinitions;\n}\n\nexport class FetchAdapter extends BaseFetchAdapter {\n private username: string;\n private password: string;\n private tokenStore: Omit<TokenStoreDefinitions, \"getKey\">;\n private getTokenKey: Required<TokenStoreDefinitions>[\"getKey\"];\n\n constructor(args: FetchAdapterOptions) {\n super({ ...args, refreshToken: true });\n this.username = args.auth.username;\n this.password = args.auth.password;\n this.tokenStore = args.tokenStore ?? memoryStore();\n this.getTokenKey =\n args.tokenStore?.getKey ?? (() => `${args.server}/${args.db}`);\n\n if (this.username === \"\") throw new Error(\"Username is required\");\n if (this.password === \"\") throw new Error(\"Password is required\");\n }\n\n /**\n * Gets a FileMaker Data API token for authentication.\n *\n * This token is **NOT** guaranteed to be valid, since it expires 15 minutes after the last use. Pass `refresh=true` to forcibly get a fresh token\n *\n * @param args.refresh - If true, forces getting a new token instead of using cached token\n * @internal This method is intended for internal use, you should not need to use it in most cases.\n */\n public override getToken = async (\n args?: GetTokenArguments,\n ): Promise<string> => {\n const { refresh = false } = args ?? {};\n let token: string | null = null;\n if (!refresh) {\n token = await this.tokenStore.getToken(this.getTokenKey());\n }\n\n if (!token) {\n const res = await fetch(`${this.baseUrl}/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${Buffer.from(\n `${this.username}:${this.password}`,\n ).toString(\"base64\")}`,\n },\n });\n\n if (!res.ok) {\n const data = await res.json();\n throw new FileMakerError(\n data.messages[0].code,\n data.messages[0].message,\n );\n }\n token = res.headers.get(\"X-FM-Data-Access-Token\");\n if (!token) throw new Error(\"Could not get token\");\n this.tokenStore.setToken(this.getTokenKey(), token);\n }\n\n return token;\n };\n\n public disconnect = async (): Promise<void> => {\n const token = await this.tokenStore.getToken(this.getTokenKey());\n if (token) {\n await this.request({\n url: `/sessions/${token}`,\n method: \"DELETE\",\n fetchOptions: {\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n },\n });\n this.tokenStore.clearToken(this.getTokenKey());\n }\n };\n}\n"],"names":[],"mappings":";;;;;;AAiBO,MAAM,qBAAqB,iBAAiB;AAAA,EAMjD,YAAY,MAA2B;;AACrC,UAAM,EAAE,GAAG,MAAM,cAAc,MAAM;AAN/B;AACA;AACA;AACA;AAsBQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAW,OACzB,SACoB;AACpB,YAAM,EAAE,UAAU,MAAM,IAAI,QAAQ,CAAC;AACrC,UAAI,QAAuB;AAC3B,UAAI,CAAC,SAAS;AACZ,gBAAQ,MAAM,KAAK,WAAW,SAAS,KAAK,aAAa;AAAA,MAAA;AAG3D,UAAI,CAAC,OAAO;AACV,cAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,aAAa;AAAA,UAClD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,eAAe,SAAS,OAAO;AAAA,cAC7B,GAAG,KAAK,QAAQ,IAAI,KAAK,QAAQ;AAAA,YAAA,EACjC,SAAS,QAAQ,CAAC;AAAA,UAAA;AAAA,QACtB,CACD;AAEG,YAAA,CAAC,IAAI,IAAI;AACL,gBAAA,OAAO,MAAM,IAAI,KAAK;AAC5B,gBAAM,IAAI;AAAA,YACR,KAAK,SAAS,CAAC,EAAE;AAAA,YACjB,KAAK,SAAS,CAAC,EAAE;AAAA,UACnB;AAAA,QAAA;AAEM,gBAAA,IAAI,QAAQ,IAAI,wBAAwB;AAChD,YAAI,CAAC,MAAa,OAAA,IAAI,MAAM,qBAAqB;AACjD,aAAK,WAAW,SAAS,KAAK,YAAA,GAAe,KAAK;AAAA,MAAA;AAG7C,aAAA;AAAA,IACT;AAEO,sCAAa,YAA2B;AAC7C,YAAM,QAAQ,MAAM,KAAK,WAAW,SAAS,KAAK,aAAa;AAC/D,UAAI,OAAO;AACT,cAAM,KAAK,QAAQ;AAAA,UACjB,KAAK,aAAa,KAAK;AAAA,UACvB,QAAQ;AAAA,UACR,cAAc;AAAA,YACZ,SAAS;AAAA,cACP,eAAe,UAAU,KAAK;AAAA,cAC9B,gBAAgB;AAAA,YAAA;AAAA,UAClB;AAAA,QACF,CACD;AACD,aAAK,WAAW,WAAW,KAAK,YAAA,CAAa;AAAA,MAAA;AAAA,IAEjD;AApEO,SAAA,WAAW,KAAK,KAAK;AACrB,SAAA,WAAW,KAAK,KAAK;AACrB,SAAA,aAAa,KAAK,cAAc,YAAY;AAC5C,SAAA,gBACH,UAAK,eAAL,mBAAiB,YAAW,MAAM,GAAG,KAAK,MAAM,IAAI,KAAK,EAAE;AAE7D,QAAI,KAAK,aAAa,GAAU,OAAA,IAAI,MAAM,sBAAsB;AAChE,QAAI,KAAK,aAAa,GAAU,OAAA,IAAI,MAAM,sBAAsB;AAAA,EAAA;AA8DpE;"}
1
+ {"version":3,"file":"fetch.js","sources":["../../../src/adapters/fetch.ts"],"sourcesContent":["import { FileMakerError } from \"../client-types.js\";\nimport memoryStore from \"../tokenStore/memory.js\";\nimport type { TokenStoreDefinitions } from \"../tokenStore/types.js\";\nimport { BaseFetchAdapter } from \"./fetch-base.js\";\nimport type { BaseFetchAdapterOptions, GetTokenArguments } from \"./fetch-base-types.js\";\n\nexport interface FetchAdapterOptions extends BaseFetchAdapterOptions {\n auth: {\n username: string;\n password: string;\n };\n tokenStore?: TokenStoreDefinitions;\n}\n\nexport class FetchAdapter extends BaseFetchAdapter {\n private readonly username: string;\n private readonly password: string;\n private readonly tokenStore: Omit<TokenStoreDefinitions, \"getKey\">;\n private readonly getTokenKey: Required<TokenStoreDefinitions>[\"getKey\"];\n\n constructor(args: FetchAdapterOptions) {\n super({ ...args, refreshToken: true });\n this.username = args.auth.username;\n this.password = args.auth.password;\n this.tokenStore = args.tokenStore ?? memoryStore();\n this.getTokenKey = args.tokenStore?.getKey ?? (() => `${args.server}/${args.db}`);\n\n if (this.username === \"\") {\n throw new Error(\"Username is required\");\n }\n if (this.password === \"\") {\n throw new Error(\"Password is required\");\n }\n }\n\n /**\n * Gets a FileMaker Data API token for authentication.\n *\n * This token is **NOT** guaranteed to be valid, since it expires 15 minutes after the last use. Pass `refresh=true` to forcibly get a fresh token\n *\n * @param args.refresh - If true, forces getting a new token instead of using cached token\n * @internal This method is intended for internal use, you should not need to use it in most cases.\n */\n override getToken = async (args?: GetTokenArguments): Promise<string> => {\n const { refresh = false } = args ?? {};\n let token: string | null = null;\n if (!refresh) {\n token = await this.tokenStore.getToken(this.getTokenKey());\n }\n\n if (!token) {\n const res = await fetch(`${this.baseUrl}/sessions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${Buffer.from(`${this.username}:${this.password}`).toString(\"base64\")}`,\n },\n });\n\n if (!res.ok) {\n const data = await res.json();\n throw new FileMakerError(data.messages[0].code, data.messages[0].message);\n }\n token = res.headers.get(\"X-FM-Data-Access-Token\");\n if (!token) {\n throw new Error(\"Could not get token\");\n }\n this.tokenStore.setToken(this.getTokenKey(), token);\n }\n\n return token;\n };\n\n disconnect = async (): Promise<void> => {\n const token = await this.tokenStore.getToken(this.getTokenKey());\n if (token) {\n await this.request({\n url: `/sessions/${token}`,\n method: \"DELETE\",\n fetchOptions: {\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n },\n });\n this.tokenStore.clearToken(this.getTokenKey());\n }\n };\n}\n"],"names":[],"mappings":";;;;;;AAcO,MAAM,qBAAqB,iBAAiB;AAAA,EAMjD,YAAY,MAA2B;;AACrC,UAAM,EAAE,GAAG,MAAM,cAAc,MAAM;AANtB;AACA;AACA;AACA;AAyBR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAW,OAAO,SAA8C;AACvE,YAAM,EAAE,UAAU,MAAA,IAAU,QAAQ,CAAA;AACpC,UAAI,QAAuB;AAC3B,UAAI,CAAC,SAAS;AACZ,gBAAQ,MAAM,KAAK,WAAW,SAAS,KAAK,aAAa;AAAA,MAC3D;AAEA,UAAI,CAAC,OAAO;AACV,cAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,aAAa;AAAA,UAClD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,eAAe,SAAS,OAAO,KAAK,GAAG,KAAK,QAAQ,IAAI,KAAK,QAAQ,EAAE,EAAE,SAAS,QAAQ,CAAC;AAAA,UAAA;AAAA,QAC7F,CACD;AAED,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,OAAO,MAAM,IAAI,KAAA;AACvB,gBAAM,IAAI,eAAe,KAAK,SAAS,CAAC,EAAE,MAAM,KAAK,SAAS,CAAC,EAAE,OAAO;AAAA,QAC1E;AACA,gBAAQ,IAAI,QAAQ,IAAI,wBAAwB;AAChD,YAAI,CAAC,OAAO;AACV,gBAAM,IAAI,MAAM,qBAAqB;AAAA,QACvC;AACA,aAAK,WAAW,SAAS,KAAK,YAAA,GAAe,KAAK;AAAA,MACpD;AAEA,aAAO;AAAA,IACT;AAEA,sCAAa,YAA2B;AACtC,YAAM,QAAQ,MAAM,KAAK,WAAW,SAAS,KAAK,aAAa;AAC/D,UAAI,OAAO;AACT,cAAM,KAAK,QAAQ;AAAA,UACjB,KAAK,aAAa,KAAK;AAAA,UACvB,QAAQ;AAAA,UACR,cAAc;AAAA,YACZ,SAAS;AAAA,cACP,eAAe,UAAU,KAAK;AAAA,cAC9B,gBAAgB;AAAA,YAAA;AAAA,UAClB;AAAA,QACF,CACD;AACD,aAAK,WAAW,WAAW,KAAK,YAAA,CAAa;AAAA,MAC/C;AAAA,IACF;AAlEE,SAAK,WAAW,KAAK,KAAK;AAC1B,SAAK,WAAW,KAAK,KAAK;AAC1B,SAAK,aAAa,KAAK,cAAc,YAAA;AACrC,SAAK,gBAAc,UAAK,eAAL,mBAAiB,YAAW,MAAM,GAAG,KAAK,MAAM,IAAI,KAAK,EAAE;AAE9E,QAAI,KAAK,aAAa,IAAI;AACxB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AACA,QAAI,KAAK,aAAa,IAAI;AACxB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAAA,EACF;AAwDF;"}
@@ -0,0 +1,32 @@
1
+ import { CreateResponse, DeleteResponse, GetResponse, LayoutMetadataResponse, ScriptResponse, UpdateResponse } from '../client-types.js';
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") */
5
+ baseUrl: string;
6
+ /** Name of the connected FileMaker file */
7
+ connectedFileName: string;
8
+ /** Name of the FM script that executes Data API calls. Defaults to "execute_data_api" */
9
+ scriptName?: string;
10
+ }
11
+ export declare class FmHttpAdapter implements Adapter {
12
+ protected baseUrl: string;
13
+ protected connectedFileName: string;
14
+ protected scriptName: string;
15
+ constructor(options: FmHttpAdapterOptions);
16
+ protected request: (params: {
17
+ layout: string;
18
+ body: object;
19
+ action?: "read" | "metaData" | "create" | "update" | "delete";
20
+ timeout?: number;
21
+ fetchOptions?: RequestInit;
22
+ }) => Promise<unknown>;
23
+ list: (opts: ListOptions) => Promise<GetResponse>;
24
+ get: (opts: GetOptions) => Promise<GetResponse>;
25
+ find: (opts: FindOptions) => Promise<GetResponse>;
26
+ create: (opts: CreateOptions) => Promise<CreateResponse>;
27
+ update: (opts: UpdateOptions) => Promise<UpdateResponse>;
28
+ delete: (opts: DeleteOptions) => Promise<DeleteResponse>;
29
+ layoutMetadata: (opts: LayoutMetadataOptions) => Promise<LayoutMetadataResponse>;
30
+ executeScript: (opts: ExecuteScriptOptions) => Promise<ScriptResponse>;
31
+ containerUpload: () => Promise<never>;
32
+ }
@@ -0,0 +1,170 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { FileMakerError } from "../client-types.js";
5
+ const TRAILING_SLASHES_REGEX = /\/+$/;
6
+ class FmHttpAdapter {
7
+ constructor(options) {
8
+ __publicField(this, "baseUrl");
9
+ __publicField(this, "connectedFileName");
10
+ __publicField(this, "scriptName");
11
+ __publicField(this, "request", async (params) => {
12
+ var _a, _b;
13
+ const { action = "read", layout, body, fetchOptions = {} } = params;
14
+ const normalizedBody = { ...body };
15
+ if ("_offset" in normalizedBody) {
16
+ normalizedBody.offset = normalizedBody._offset;
17
+ normalizedBody._offset = void 0;
18
+ }
19
+ if ("_limit" in normalizedBody) {
20
+ normalizedBody.limit = normalizedBody._limit;
21
+ normalizedBody._limit = void 0;
22
+ }
23
+ if ("_sort" in normalizedBody) {
24
+ normalizedBody.sort = normalizedBody._sort;
25
+ normalizedBody._sort = void 0;
26
+ }
27
+ const scriptParam = JSON.stringify({
28
+ ...normalizedBody,
29
+ layouts: layout,
30
+ action,
31
+ version: "vLatest"
32
+ });
33
+ const controller = new AbortController();
34
+ let timeout = null;
35
+ if (params.timeout) {
36
+ timeout = setTimeout(() => controller.abort(), params.timeout);
37
+ }
38
+ const headers = new Headers(fetchOptions == null ? void 0 : fetchOptions.headers);
39
+ headers.set("Content-Type", "application/json");
40
+ let res;
41
+ try {
42
+ res = await fetch(`${this.baseUrl}/callScript`, {
43
+ ...fetchOptions,
44
+ method: "POST",
45
+ headers,
46
+ body: JSON.stringify({
47
+ connectedFileName: this.connectedFileName,
48
+ scriptName: this.scriptName,
49
+ data: scriptParam
50
+ }),
51
+ signal: controller.signal
52
+ });
53
+ } finally {
54
+ if (timeout) {
55
+ clearTimeout(timeout);
56
+ }
57
+ }
58
+ if (!res.ok) {
59
+ throw new FileMakerError(String(res.status), `FM HTTP request failed (${res.status}): ${await res.text()}`);
60
+ }
61
+ const raw = await res.json();
62
+ let scriptResult;
63
+ try {
64
+ scriptResult = typeof raw.result === "string" ? JSON.parse(raw.result) : raw.result ?? raw;
65
+ } catch (err) {
66
+ throw new FileMakerError(
67
+ "500",
68
+ `FM HTTP response parse failed: ${err instanceof Error ? err.message : String(err)}`
69
+ );
70
+ }
71
+ const respData = scriptResult;
72
+ const errorCode = (_b = (_a = respData.messages) == null ? void 0 : _a[0]) == null ? void 0 : _b.code;
73
+ if (errorCode !== void 0 && errorCode !== "0") {
74
+ throw new FileMakerError(
75
+ errorCode,
76
+ `Filemaker Data API failed with (${errorCode}): ${JSON.stringify(respData, null, 2)}`
77
+ );
78
+ }
79
+ return respData.response;
80
+ });
81
+ __publicField(this, "list", async (opts) => {
82
+ return await this.request({
83
+ body: opts.data,
84
+ layout: opts.layout,
85
+ timeout: opts.timeout,
86
+ fetchOptions: opts.fetch
87
+ });
88
+ });
89
+ __publicField(this, "get", async (opts) => {
90
+ return await this.request({
91
+ body: opts.data,
92
+ layout: opts.layout,
93
+ timeout: opts.timeout,
94
+ fetchOptions: opts.fetch
95
+ });
96
+ });
97
+ __publicField(this, "find", async (opts) => {
98
+ return await this.request({
99
+ body: opts.data,
100
+ layout: opts.layout,
101
+ timeout: opts.timeout,
102
+ fetchOptions: opts.fetch
103
+ });
104
+ });
105
+ __publicField(this, "create", async (opts) => {
106
+ return await this.request({
107
+ action: "create",
108
+ body: opts.data,
109
+ layout: opts.layout,
110
+ timeout: opts.timeout,
111
+ fetchOptions: opts.fetch
112
+ });
113
+ });
114
+ __publicField(this, "update", async (opts) => {
115
+ return await this.request({
116
+ action: "update",
117
+ body: opts.data,
118
+ layout: opts.layout,
119
+ timeout: opts.timeout,
120
+ fetchOptions: opts.fetch
121
+ });
122
+ });
123
+ __publicField(this, "delete", async (opts) => {
124
+ return await this.request({
125
+ action: "delete",
126
+ body: opts.data,
127
+ layout: opts.layout,
128
+ timeout: opts.timeout,
129
+ fetchOptions: opts.fetch
130
+ });
131
+ });
132
+ __publicField(this, "layoutMetadata", async (opts) => {
133
+ return await this.request({
134
+ action: "metaData",
135
+ layout: opts.layout,
136
+ body: {},
137
+ timeout: opts.timeout,
138
+ fetchOptions: opts.fetch
139
+ });
140
+ });
141
+ __publicField(this, "executeScript", async (opts) => {
142
+ const res = await fetch(`${this.baseUrl}/callScript`, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify({
146
+ connectedFileName: this.connectedFileName,
147
+ scriptName: opts.script,
148
+ data: opts.scriptParam
149
+ })
150
+ });
151
+ if (!res.ok) {
152
+ throw new FileMakerError(String(res.status), `FM HTTP executeScript failed (${res.status}): ${await res.text()}`);
153
+ }
154
+ const raw = await res.json();
155
+ return {
156
+ scriptResult: typeof raw.result === "string" ? raw.result : JSON.stringify(raw.result)
157
+ };
158
+ });
159
+ __publicField(this, "containerUpload", () => {
160
+ throw new Error("Container upload is not supported via FM HTTP adapter");
161
+ });
162
+ this.baseUrl = options.baseUrl.replace(TRAILING_SLASHES_REGEX, "");
163
+ this.connectedFileName = options.connectedFileName;
164
+ this.scriptName = options.scriptName ?? "execute_data_api";
165
+ }
166
+ }
167
+ export {
168
+ FmHttpAdapter
169
+ };
170
+ //# sourceMappingURL=fm-http.js.map
@@ -0,0 +1 @@
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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"}
@@ -18,8 +18,8 @@ export type OttoAdapterOptions = BaseFetchAdapterOptions & {
18
18
  auth: OttoAuth;
19
19
  };
20
20
  export declare class OttoAdapter extends BaseFetchAdapter {
21
- private apiKey;
22
- private port;
21
+ private readonly apiKey;
22
+ private readonly port;
23
23
  constructor(options: OttoAdapterOptions);
24
24
  protected getToken: () => Promise<string>;
25
25
  }