@proofkit/fmodata 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (162) hide show
  1. package/README.md +1250 -377
  2. package/dist/esm/client/batch-builder.d.ts +56 -0
  3. package/dist/esm/client/batch-builder.js +238 -0
  4. package/dist/esm/client/batch-builder.js.map +1 -0
  5. package/dist/esm/client/batch-request.d.ts +61 -0
  6. package/dist/esm/client/batch-request.js +252 -0
  7. package/dist/esm/client/batch-request.js.map +1 -0
  8. package/dist/esm/client/builders/default-select.d.ts +10 -0
  9. package/dist/esm/client/builders/default-select.js +43 -0
  10. package/dist/esm/client/builders/default-select.js.map +1 -0
  11. package/dist/esm/client/builders/expand-builder.d.ts +45 -0
  12. package/dist/esm/client/builders/expand-builder.js +174 -0
  13. package/dist/esm/client/builders/expand-builder.js.map +1 -0
  14. package/dist/esm/client/builders/index.d.ts +8 -0
  15. package/dist/esm/client/builders/query-string-builder.d.ts +18 -0
  16. package/dist/esm/client/builders/query-string-builder.js +25 -0
  17. package/dist/esm/client/builders/query-string-builder.js.map +1 -0
  18. package/dist/esm/client/builders/response-processor.d.ts +43 -0
  19. package/dist/esm/client/builders/response-processor.js +176 -0
  20. package/dist/esm/client/builders/response-processor.js.map +1 -0
  21. package/dist/esm/client/builders/select-mixin.d.ts +32 -0
  22. package/dist/esm/client/builders/select-mixin.js +30 -0
  23. package/dist/esm/client/builders/select-mixin.js.map +1 -0
  24. package/dist/esm/client/builders/select-utils.d.ts +18 -0
  25. package/dist/esm/client/builders/select-utils.js +23 -0
  26. package/dist/esm/client/builders/select-utils.js.map +1 -0
  27. package/dist/esm/client/builders/shared-types.d.ts +40 -0
  28. package/dist/esm/client/builders/table-utils.d.ts +35 -0
  29. package/dist/esm/client/builders/table-utils.js +45 -0
  30. package/dist/esm/client/builders/table-utils.js.map +1 -0
  31. package/dist/esm/client/database.d.ts +68 -15
  32. package/dist/esm/client/database.js +88 -34
  33. package/dist/esm/client/database.js.map +1 -1
  34. package/dist/esm/client/delete-builder.d.ts +31 -17
  35. package/dist/esm/client/delete-builder.js +114 -47
  36. package/dist/esm/client/delete-builder.js.map +1 -1
  37. package/dist/esm/client/entity-set.d.ts +33 -27
  38. package/dist/esm/client/entity-set.js +123 -45
  39. package/dist/esm/client/entity-set.js.map +1 -1
  40. package/dist/esm/client/error-parser.d.ts +12 -0
  41. package/dist/esm/client/error-parser.js +30 -0
  42. package/dist/esm/client/error-parser.js.map +1 -0
  43. package/dist/esm/client/filemaker-odata.d.ts +44 -6
  44. package/dist/esm/client/filemaker-odata.js +172 -28
  45. package/dist/esm/client/filemaker-odata.js.map +1 -1
  46. package/dist/esm/client/insert-builder.d.ts +39 -9
  47. package/dist/esm/client/insert-builder.js +265 -36
  48. package/dist/esm/client/insert-builder.js.map +1 -1
  49. package/dist/esm/client/query/expand-builder.d.ts +35 -0
  50. package/dist/esm/client/query/index.d.ts +3 -0
  51. package/dist/esm/client/query/query-builder.d.ts +139 -0
  52. package/dist/esm/client/query/query-builder.js +481 -0
  53. package/dist/esm/client/query/query-builder.js.map +1 -0
  54. package/dist/esm/client/query/response-processor.d.ts +25 -0
  55. package/dist/esm/client/query/types.d.ts +77 -0
  56. package/dist/esm/client/query/url-builder.d.ts +71 -0
  57. package/dist/esm/client/query/url-builder.js +107 -0
  58. package/dist/esm/client/query/url-builder.js.map +1 -0
  59. package/dist/esm/client/query-builder.d.ts +1 -94
  60. package/dist/esm/client/record-builder.d.ts +107 -22
  61. package/dist/esm/client/record-builder.js +342 -64
  62. package/dist/esm/client/record-builder.js.map +1 -1
  63. package/dist/esm/client/response-processor.d.ts +33 -0
  64. package/dist/esm/client/sanitize-json.d.ts +35 -0
  65. package/dist/esm/client/sanitize-json.js +27 -0
  66. package/dist/esm/client/sanitize-json.js.map +1 -0
  67. package/dist/esm/client/schema-manager.d.ts +57 -0
  68. package/dist/esm/client/schema-manager.js +132 -0
  69. package/dist/esm/client/schema-manager.js.map +1 -0
  70. package/dist/esm/client/update-builder.d.ts +42 -25
  71. package/dist/esm/client/update-builder.js +179 -46
  72. package/dist/esm/client/update-builder.js.map +1 -1
  73. package/dist/esm/client/webhook-builder.d.ts +126 -0
  74. package/dist/esm/client/webhook-builder.js +197 -0
  75. package/dist/esm/client/webhook-builder.js.map +1 -0
  76. package/dist/esm/errors.d.ts +90 -0
  77. package/dist/esm/errors.js +180 -0
  78. package/dist/esm/errors.js.map +1 -0
  79. package/dist/esm/index.d.ts +12 -4
  80. package/dist/esm/index.js +59 -6
  81. package/dist/esm/index.js.map +1 -1
  82. package/dist/esm/logger.d.ts +47 -0
  83. package/dist/esm/logger.js +72 -0
  84. package/dist/esm/logger.js.map +1 -0
  85. package/dist/esm/logger.test.d.ts +1 -0
  86. package/dist/esm/orm/column.d.ts +62 -0
  87. package/dist/esm/orm/column.js +62 -0
  88. package/dist/esm/orm/column.js.map +1 -0
  89. package/dist/esm/orm/field-builders.d.ts +164 -0
  90. package/dist/esm/orm/field-builders.js +168 -0
  91. package/dist/esm/orm/field-builders.js.map +1 -0
  92. package/dist/esm/orm/index.d.ts +4 -0
  93. package/dist/esm/orm/operators.d.ts +175 -0
  94. package/dist/esm/orm/operators.js +242 -0
  95. package/dist/esm/orm/operators.js.map +1 -0
  96. package/dist/esm/orm/table.d.ts +355 -0
  97. package/dist/esm/orm/table.js +200 -0
  98. package/dist/esm/orm/table.js.map +1 -0
  99. package/dist/esm/transform.d.ts +64 -0
  100. package/dist/esm/transform.js +110 -0
  101. package/dist/esm/transform.js.map +1 -0
  102. package/dist/esm/types.d.ts +157 -7
  103. package/dist/esm/types.js +7 -0
  104. package/dist/esm/types.js.map +1 -0
  105. package/dist/esm/validation.d.ts +22 -9
  106. package/dist/esm/validation.js +195 -50
  107. package/dist/esm/validation.js.map +1 -1
  108. package/package.json +19 -4
  109. package/src/client/batch-builder.ts +334 -0
  110. package/src/client/batch-request.ts +485 -0
  111. package/src/client/builders/default-select.ts +80 -0
  112. package/src/client/builders/expand-builder.ts +245 -0
  113. package/src/client/builders/index.ts +11 -0
  114. package/src/client/builders/query-string-builder.ts +49 -0
  115. package/src/client/builders/response-processor.ts +286 -0
  116. package/src/client/builders/select-mixin.ts +75 -0
  117. package/src/client/builders/select-utils.ts +56 -0
  118. package/src/client/builders/shared-types.ts +42 -0
  119. package/src/client/builders/table-utils.ts +87 -0
  120. package/src/client/database.ts +147 -89
  121. package/src/client/delete-builder.ts +189 -87
  122. package/src/client/entity-set.ts +316 -205
  123. package/src/client/error-parser.ts +59 -0
  124. package/src/client/filemaker-odata.ts +254 -41
  125. package/src/client/insert-builder.ts +420 -49
  126. package/src/client/query/expand-builder.ts +164 -0
  127. package/src/client/query/index.ts +13 -0
  128. package/src/client/query/query-builder.ts +905 -0
  129. package/src/client/query/response-processor.ts +236 -0
  130. package/src/client/query/types.ts +128 -0
  131. package/src/client/query/url-builder.ts +179 -0
  132. package/src/client/query-builder.ts +8 -1076
  133. package/src/client/record-builder.ts +704 -139
  134. package/src/client/response-processor.ts +89 -0
  135. package/src/client/sanitize-json.ts +66 -0
  136. package/src/client/schema-manager.ts +246 -0
  137. package/src/client/update-builder.ts +318 -90
  138. package/src/client/webhook-builder.ts +285 -0
  139. package/src/errors.ts +261 -0
  140. package/src/index.ts +122 -14
  141. package/src/logger.test.ts +34 -0
  142. package/src/logger.ts +140 -0
  143. package/src/orm/column.ts +106 -0
  144. package/src/orm/field-builders.ts +318 -0
  145. package/src/orm/index.ts +60 -0
  146. package/src/orm/operators.ts +487 -0
  147. package/src/orm/table.ts +759 -0
  148. package/src/transform.ts +263 -0
  149. package/src/types.ts +275 -55
  150. package/src/validation.ts +255 -55
  151. package/dist/esm/client/base-table.d.ts +0 -13
  152. package/dist/esm/client/base-table.js +0 -19
  153. package/dist/esm/client/base-table.js.map +0 -1
  154. package/dist/esm/client/query-builder.js +0 -649
  155. package/dist/esm/client/query-builder.js.map +0 -1
  156. package/dist/esm/client/table-occurrence.d.ts +0 -25
  157. package/dist/esm/client/table-occurrence.js +0 -47
  158. package/dist/esm/client/table-occurrence.js.map +0 -1
  159. package/dist/esm/filter-types.d.ts +0 -76
  160. package/src/client/base-table.ts +0 -25
  161. package/src/client/table-occurrence.ts +0 -100
  162. package/src/filter-types.ts +0 -97
@@ -1,13 +1,22 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import createClient from "@fetchkit/ffetch";
4
+ import createClient, { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError } from "@fetchkit/ffetch";
5
+ import { getAcceptHeader } from "../types.js";
6
+ import { SchemaLockedError, ODataError, HTTPError, ResponseParseError } from "../errors.js";
5
7
  import { Database } from "./database.js";
8
+ import { safeJsonParse } from "./sanitize-json.js";
9
+ import { get } from "es-toolkit/compat";
10
+ import { createLogger } from "../logger.js";
6
11
  class FMServerConnection {
7
12
  constructor(config) {
8
13
  __publicField(this, "fetchClient");
9
14
  __publicField(this, "serverUrl");
10
15
  __publicField(this, "auth");
16
+ __publicField(this, "useEntityIds", false);
17
+ __publicField(this, "includeSpecialColumns", false);
18
+ __publicField(this, "logger");
19
+ this.logger = createLogger(config.logger);
11
20
  this.fetchClient = createClient({
12
21
  retries: 0,
13
22
  ...config.fetchClientOptions
@@ -20,18 +29,75 @@ class FMServerConnection {
20
29
  this.serverUrl = url.toString().replace(/\/+$/, "");
21
30
  this.auth = config.auth;
22
31
  }
32
+ /**
33
+ * @internal
34
+ * Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
35
+ */
36
+ _setUseEntityIds(useEntityIds) {
37
+ this.useEntityIds = useEntityIds;
38
+ }
39
+ /**
40
+ * @internal
41
+ * Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
42
+ */
43
+ _getUseEntityIds() {
44
+ return this.useEntityIds;
45
+ }
46
+ /**
47
+ * @internal
48
+ * Sets whether to include special columns (ROWID and ROWMODID) in requests
49
+ */
50
+ _setIncludeSpecialColumns(includeSpecialColumns) {
51
+ this.includeSpecialColumns = includeSpecialColumns;
52
+ }
53
+ /**
54
+ * @internal
55
+ * Gets whether to include special columns (ROWID and ROWMODID) in requests
56
+ */
57
+ _getIncludeSpecialColumns() {
58
+ return this.includeSpecialColumns;
59
+ }
60
+ /**
61
+ * @internal
62
+ * Gets the base URL for OData requests
63
+ */
64
+ _getBaseUrl() {
65
+ return `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
66
+ }
67
+ /**
68
+ * @internal
69
+ * Gets the logger instance
70
+ */
71
+ _getLogger() {
72
+ return this.logger;
73
+ }
23
74
  /**
24
75
  * @internal
25
76
  */
26
77
  async _makeRequest(url, options) {
27
- var _a;
78
+ var _a, _b, _c, _d, _e, _f;
79
+ const logger = this._getLogger();
28
80
  const baseUrl = `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
81
+ const fullUrl = baseUrl + url;
82
+ const useEntityIds = (options == null ? void 0 : options.useEntityIds) ?? this.useEntityIds;
83
+ const includeSpecialColumns = (options == null ? void 0 : options.includeSpecialColumns) ?? this.includeSpecialColumns;
84
+ const includeODataAnnotations = options == null ? void 0 : options.includeODataAnnotations;
85
+ const preferValues = [];
86
+ if (useEntityIds) {
87
+ preferValues.push("fmodata.entity-ids");
88
+ }
89
+ if (includeSpecialColumns) {
90
+ preferValues.push("fmodata.include-specialcolumns");
91
+ }
29
92
  const headers = {
30
93
  Authorization: "apiKey" in this.auth ? `Bearer ${this.auth.apiKey}` : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,
31
94
  "Content-Type": "application/json",
32
- Accept: "application/json",
95
+ Accept: getAcceptHeader(includeODataAnnotations),
96
+ ...preferValues.length > 0 ? { Prefer: preferValues.join(", ") } : {},
33
97
  ...(options == null ? void 0 : options.headers) || {}
34
98
  };
99
+ const { Authorization, ...loggableHeaders } = headers;
100
+ logger.debug("Request headers:", loggableHeaders);
35
101
  const fetchHandler = options == null ? void 0 : options.fetchHandler;
36
102
  const {
37
103
  headers: _headers,
@@ -39,30 +105,105 @@ class FMServerConnection {
39
105
  ...restOptions
40
106
  } = options || {};
41
107
  const clientToUse = fetchHandler ? createClient({ retries: 0, fetchHandler }) : this.fetchClient;
42
- const resp = await clientToUse(baseUrl + url, {
43
- ...restOptions,
44
- headers
45
- });
46
- if (!resp.ok) {
47
- throw new Error(
48
- `Failed to make request to ${baseUrl + url}: ${resp.statusText}`
49
- );
50
- }
51
- const affectedRows = resp.headers.get("fmodata.affected_rows");
52
- if (affectedRows !== null) {
53
- return parseInt(affectedRows, 10);
54
- }
55
- if (resp.status === 204) {
56
- return 0;
57
- }
58
- if ((_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
59
- let data = await resp.json();
60
- if (data.error) {
61
- throw new Error(data.error);
108
+ try {
109
+ const finalOptions = {
110
+ ...restOptions,
111
+ headers
112
+ };
113
+ const resp = await clientToUse(fullUrl, finalOptions);
114
+ logger.debug(`${finalOptions.method ?? "GET"} ${resp.status} ${fullUrl}`);
115
+ if (!resp.ok) {
116
+ let errorBody;
117
+ try {
118
+ if ((_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
119
+ errorBody = await safeJsonParse(resp);
120
+ }
121
+ } catch {
122
+ }
123
+ if (errorBody == null ? void 0 : errorBody.error) {
124
+ const errorCode = errorBody.error.code;
125
+ const errorMessage = errorBody.error.message || resp.statusText;
126
+ if (errorCode === "303" || errorCode === 303) {
127
+ return {
128
+ data: void 0,
129
+ error: new SchemaLockedError(
130
+ fullUrl,
131
+ errorMessage,
132
+ errorBody.error
133
+ )
134
+ };
135
+ }
136
+ return {
137
+ data: void 0,
138
+ error: new ODataError(
139
+ fullUrl,
140
+ errorMessage,
141
+ String(errorCode),
142
+ errorBody.error
143
+ )
144
+ };
145
+ }
146
+ return {
147
+ data: void 0,
148
+ error: new HTTPError(
149
+ fullUrl,
150
+ resp.status,
151
+ resp.statusText,
152
+ errorBody
153
+ )
154
+ };
155
+ }
156
+ const affectedRows = resp.headers.get("fmodata.affected_rows");
157
+ if (affectedRows !== null) {
158
+ return { data: parseInt(affectedRows, 10), error: void 0 };
159
+ }
160
+ if (resp.status === 204) {
161
+ const locationHeader = ((_c = (_b = resp.headers) == null ? void 0 : _b.get) == null ? void 0 : _c.call(_b, "Location")) || ((_e = (_d = resp.headers) == null ? void 0 : _d.get) == null ? void 0 : _e.call(_d, "location"));
162
+ if (locationHeader) {
163
+ return { data: { _location: locationHeader }, error: void 0 };
164
+ }
165
+ return { data: 0, error: void 0 };
62
166
  }
63
- return data;
167
+ if ((_f = resp.headers.get("content-type")) == null ? void 0 : _f.includes("application/json")) {
168
+ const data = await safeJsonParse(resp);
169
+ if (get(data, "error", null)) {
170
+ const errorCode = get(data, "error.code", null);
171
+ const errorMessage = get(
172
+ data,
173
+ "error.message",
174
+ "Unknown OData error"
175
+ );
176
+ if (errorCode === "303" || errorCode === 303) {
177
+ return {
178
+ data: void 0,
179
+ error: new SchemaLockedError(fullUrl, errorMessage, data.error)
180
+ };
181
+ }
182
+ return {
183
+ data: void 0,
184
+ error: new ODataError(
185
+ fullUrl,
186
+ errorMessage,
187
+ String(errorCode),
188
+ data.error
189
+ )
190
+ };
191
+ }
192
+ return { data, error: void 0 };
193
+ }
194
+ return { data: await resp.text(), error: void 0 };
195
+ } catch (err) {
196
+ if (err instanceof TimeoutError || err instanceof AbortError || err instanceof NetworkError || err instanceof RetryLimitError || err instanceof CircuitOpenError) {
197
+ return { data: void 0, error: err };
198
+ }
199
+ if (err instanceof ResponseParseError) {
200
+ return { data: void 0, error: err };
201
+ }
202
+ return {
203
+ data: void 0,
204
+ error: new NetworkError(fullUrl, err)
205
+ };
64
206
  }
65
- return await resp.text();
66
207
  }
67
208
  database(name, config) {
68
209
  return new Database(name, this, config);
@@ -72,9 +213,12 @@ class FMServerConnection {
72
213
  * @returns Promise resolving to an array of database names
73
214
  */
74
215
  async listDatabaseNames() {
75
- const response = await this._makeRequest("/");
76
- if (response.value && Array.isArray(response.value)) {
77
- return response.value.map((item) => item.name);
216
+ const result = await this._makeRequest("/$metadata", { headers: { Accept: "application/json" } });
217
+ if (result.error) {
218
+ throw result.error;
219
+ }
220
+ if (result.data.value && Array.isArray(result.data.value)) {
221
+ return result.data.value.map((item) => item.name);
78
222
  }
79
223
  return [];
80
224
  }
@@ -1 +1 @@
1
- {"version":3,"file":"filemaker-odata.js","sources":["../../../src/client/filemaker-odata.ts"],"sourcesContent":["import createClient, { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { Auth, ExecutionContext } from \"../types\";\nimport { Database } from \"./database\";\nimport { TableOccurrence } from \"./table-occurrence\";\n\nexport class FMServerConnection implements ExecutionContext {\n private fetchClient: ReturnType<typeof createClient>;\n private serverUrl: string;\n private auth: Auth;\n constructor(config: {\n serverUrl: string;\n auth: Auth;\n fetchClientOptions?: FFetchOptions;\n }) {\n this.fetchClient = createClient({\n retries: 0,\n ...config.fetchClientOptions,\n });\n // Ensure the URL uses https://, is valid, and has no trailing slash\n const url = new URL(config.serverUrl);\n if (url.protocol !== \"https:\") {\n url.protocol = \"https:\";\n }\n // Remove any trailing slash from pathname\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n this.serverUrl = url.toString().replace(/\\/+$/, \"\");\n this.auth = config.auth;\n }\n\n /**\n * @internal\n */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions,\n ): Promise<T> {\n const baseUrl = `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n\n const headers = {\n Authorization:\n \"apiKey\" in this.auth\n ? `Bearer ${this.auth.apiKey}`\n : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(options?.headers || {}),\n };\n\n // TEMPORARY WORKAROUND: Hopefully this feature will be fixed in the ffetch library\n // Extract fetchHandler and headers separately, only for tests where we're overriding the fetch handler per-request\n const fetchHandler = options?.fetchHandler;\n const {\n headers: _headers,\n fetchHandler: _fetchHandler,\n ...restOptions\n } = options || {};\n\n // If fetchHandler is provided, create a temporary client with it\n // Otherwise use the existing client\n const clientToUse = fetchHandler\n ? createClient({ retries: 0, fetchHandler })\n : this.fetchClient;\n\n const resp = await clientToUse(baseUrl + url, {\n ...restOptions,\n headers,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Failed to make request to ${baseUrl + url}: ${resp.statusText}`,\n );\n }\n\n // Check for affected rows header (for DELETE and bulk PATCH operations)\n // FileMaker may return this with 204 No Content or 200 OK\n const affectedRows = resp.headers.get(\"fmodata.affected_rows\");\n if (affectedRows !== null) {\n return parseInt(affectedRows, 10) as T;\n }\n\n // Handle 204 No Content with no body\n if (resp.status === 204) {\n return 0 as T;\n }\n\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n let data = await resp.json();\n if (data.error) {\n throw new Error(data.error);\n }\n return data as T;\n }\n return (await resp.text()) as T;\n }\n\n database<\n const Occurrences extends readonly TableOccurrence<any, any, any, any>[],\n >(\n name: string,\n config?: { occurrences?: Occurrences },\n ): Database<Occurrences> {\n return new Database(name, this, config);\n }\n\n /**\n * Lists all available databases from the FileMaker OData service.\n * @returns Promise resolving to an array of database names\n */\n async listDatabaseNames(): Promise<string[]> {\n const response = (await this._makeRequest(\"/\")) as {\n value?: Array<{ name: string }>;\n };\n if (response.value && Array.isArray(response.value)) {\n return response.value.map((item) => item.name);\n }\n return [];\n }\n}\n"],"names":[],"mappings":";;;;;AAKO,MAAM,mBAA+C;AAAA,EAI1D,YAAY,QAIT;AAPK;AACA;AACA;AAMN,SAAK,cAAc,aAAa;AAAA,MAC9B,SAAS;AAAA,MACT,GAAG,OAAO;AAAA,IAAA,CACX;AAED,UAAM,MAAM,IAAI,IAAI,OAAO,SAAS;AAChC,QAAA,IAAI,aAAa,UAAU;AAC7B,UAAI,WAAW;AAAA,IAAA;AAGjB,QAAI,WAAW,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAC9C,SAAK,YAAY,IAAI,SAAW,EAAA,QAAQ,QAAQ,EAAE;AAClD,SAAK,OAAO,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMrB,MAAM,aACJ,KACA,SACY;;AACN,UAAA,UAAU,GAAG,KAAK,SAAS,GAAG,YAAY,KAAK,OAAO,UAAU,EAAE;AAExE,UAAM,UAAU;AAAA,MACd,eACE,YAAY,KAAK,OACb,UAAU,KAAK,KAAK,MAAM,KAC1B,SAAS,KAAK,GAAG,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;AAAA,MAClE,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,IAAI,mCAAS,YAAW,CAAA;AAAA,IAC1B;AAIA,UAAM,eAAe,mCAAS;AACxB,UAAA;AAAA,MACJ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,GAAG;AAAA,IACL,IAAI,WAAW,CAAC;AAIV,UAAA,cAAc,eAChB,aAAa,EAAE,SAAS,GAAG,aAAA,CAAc,IACzC,KAAK;AAET,UAAM,OAAO,MAAM,YAAY,UAAU,KAAK;AAAA,MAC5C,GAAG;AAAA,MACH;AAAA,IAAA,CACD;AAEG,QAAA,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI;AAAA,QACR,6BAA6B,UAAU,GAAG,KAAK,KAAK,UAAU;AAAA,MAChE;AAAA,IAAA;AAKF,UAAM,eAAe,KAAK,QAAQ,IAAI,uBAAuB;AAC7D,QAAI,iBAAiB,MAAM;AAClB,aAAA,SAAS,cAAc,EAAE;AAAA,IAAA;AAI9B,QAAA,KAAK,WAAW,KAAK;AAChB,aAAA;AAAA,IAAA;AAGT,SAAI,UAAK,QAAQ,IAAI,cAAc,MAA/B,mBAAkC,SAAS,qBAAqB;AAC9D,UAAA,OAAO,MAAM,KAAK,KAAK;AAC3B,UAAI,KAAK,OAAO;AACR,cAAA,IAAI,MAAM,KAAK,KAAK;AAAA,MAAA;AAErB,aAAA;AAAA,IAAA;AAED,WAAA,MAAM,KAAK,KAAK;AAAA,EAAA;AAAA,EAG1B,SAGE,MACA,QACuB;AACvB,WAAO,IAAI,SAAS,MAAM,MAAM,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxC,MAAM,oBAAuC;AAC3C,UAAM,WAAY,MAAM,KAAK,aAAa,GAAG;AAG7C,QAAI,SAAS,SAAS,MAAM,QAAQ,SAAS,KAAK,GAAG;AACnD,aAAO,SAAS,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,IAAA;AAE/C,WAAO,CAAC;AAAA,EAAA;AAEZ;"}
1
+ {"version":3,"file":"filemaker-odata.js","sources":["../../../src/client/filemaker-odata.ts"],"sourcesContent":["import createClient, {\n FFetchOptions,\n TimeoutError,\n AbortError,\n NetworkError,\n RetryLimitError,\n CircuitOpenError,\n} from \"@fetchkit/ffetch\";\nimport type { Auth, ExecutionContext, Result } from \"../types\";\nimport { getAcceptHeader } from \"../types\";\nimport {\n HTTPError,\n ODataError,\n SchemaLockedError,\n ResponseParseError,\n} from \"../errors\";\nimport { Database } from \"./database\";\nimport { safeJsonParse } from \"./sanitize-json\";\nimport { get } from \"es-toolkit/compat\";\nimport { createLogger, type Logger, type InternalLogger } from \"../logger\";\n\nexport class FMServerConnection implements ExecutionContext {\n private fetchClient: ReturnType<typeof createClient>;\n private serverUrl: string;\n private auth: Auth;\n private useEntityIds: boolean = false;\n private includeSpecialColumns: boolean = false;\n private logger: InternalLogger;\n constructor(config: {\n serverUrl: string;\n auth: Auth;\n fetchClientOptions?: FFetchOptions;\n logger?: Logger;\n }) {\n this.logger = createLogger(config.logger);\n this.fetchClient = createClient({\n retries: 0,\n ...config.fetchClientOptions,\n });\n // Ensure the URL uses https://, is valid, and has no trailing slash\n const url = new URL(config.serverUrl);\n if (url.protocol !== \"https:\") {\n url.protocol = \"https:\";\n }\n // Remove any trailing slash from pathname\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n this.serverUrl = url.toString().replace(/\\/+$/, \"\");\n this.auth = config.auth;\n }\n\n /**\n * @internal\n * Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests\n */\n _setUseEntityIds(useEntityIds: boolean): void {\n this.useEntityIds = useEntityIds;\n }\n\n /**\n * @internal\n * Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests\n */\n _getUseEntityIds(): boolean {\n return this.useEntityIds;\n }\n\n /**\n * @internal\n * Sets whether to include special columns (ROWID and ROWMODID) in requests\n */\n _setIncludeSpecialColumns(includeSpecialColumns: boolean): void {\n this.includeSpecialColumns = includeSpecialColumns;\n }\n\n /**\n * @internal\n * Gets whether to include special columns (ROWID and ROWMODID) in requests\n */\n _getIncludeSpecialColumns(): boolean {\n return this.includeSpecialColumns;\n }\n\n /**\n * @internal\n * Gets the base URL for OData requests\n */\n _getBaseUrl(): string {\n return `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n }\n\n /**\n * @internal\n * Gets the logger instance\n */\n _getLogger(): InternalLogger {\n return this.logger;\n }\n\n /**\n * @internal\n */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit &\n FFetchOptions & {\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n },\n ): Promise<Result<T>> {\n const logger = this._getLogger();\n const baseUrl = `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n const fullUrl = baseUrl + url;\n\n // Use per-request override if provided, otherwise use the database-level setting\n const useEntityIds = options?.useEntityIds ?? this.useEntityIds;\n const includeSpecialColumns =\n options?.includeSpecialColumns ?? this.includeSpecialColumns;\n\n // Get includeODataAnnotations from options (it's passed through from execute options)\n const includeODataAnnotations = (options as any)?.includeODataAnnotations;\n\n // Build Prefer header as comma-separated list when multiple preferences are set\n const preferValues: string[] = [];\n if (useEntityIds) {\n preferValues.push(\"fmodata.entity-ids\");\n }\n if (includeSpecialColumns) {\n preferValues.push(\"fmodata.include-specialcolumns\");\n }\n\n const headers = {\n Authorization:\n \"apiKey\" in this.auth\n ? `Bearer ${this.auth.apiKey}`\n : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,\n \"Content-Type\": \"application/json\",\n Accept: getAcceptHeader(includeODataAnnotations),\n ...(preferValues.length > 0 ? { Prefer: preferValues.join(\", \") } : {}),\n ...(options?.headers || {}),\n };\n\n // Prepare loggableHeaders by omitting the Authorization key\n const { Authorization, ...loggableHeaders } = headers;\n logger.debug(\"Request headers:\", loggableHeaders);\n\n // TEMPORARY WORKAROUND: Hopefully this feature will be fixed in the ffetch library\n // Extract fetchHandler and headers separately, only for tests where we're overriding the fetch handler per-request\n const fetchHandler = options?.fetchHandler;\n const {\n headers: _headers,\n fetchHandler: _fetchHandler,\n ...restOptions\n } = options || {};\n\n // If fetchHandler is provided, create a temporary client with it\n // Otherwise use the existing client\n const clientToUse = fetchHandler\n ? createClient({ retries: 0, fetchHandler })\n : this.fetchClient;\n\n try {\n const finalOptions = {\n ...restOptions,\n headers,\n };\n\n const resp = await clientToUse(fullUrl, finalOptions);\n logger.debug(`${finalOptions.method ?? \"GET\"} ${resp.status} ${fullUrl}`);\n\n // Handle HTTP errors\n if (!resp.ok) {\n // Try to parse error body if it's JSON\n let errorBody:\n | { error?: { code?: string | number; message?: string } }\n | undefined;\n try {\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n errorBody = await safeJsonParse<typeof errorBody>(resp);\n }\n } catch {\n // Ignore JSON parse errors\n }\n\n // Check if it's an OData error response\n if (errorBody?.error) {\n const errorCode = errorBody.error.code;\n const errorMessage = errorBody.error.message || resp.statusText;\n\n // Check for schema locked error (code 303)\n if (errorCode === \"303\" || errorCode === 303) {\n return {\n data: undefined,\n error: new SchemaLockedError(\n fullUrl,\n errorMessage,\n errorBody.error,\n ),\n };\n }\n\n return {\n data: undefined,\n error: new ODataError(\n fullUrl,\n errorMessage,\n String(errorCode),\n errorBody.error,\n ),\n };\n }\n\n return {\n data: undefined,\n error: new HTTPError(\n fullUrl,\n resp.status,\n resp.statusText,\n errorBody,\n ),\n };\n }\n\n // Check for affected rows header (for DELETE and bulk PATCH operations)\n // FileMaker may return this with 204 No Content or 200 OK\n const affectedRows = resp.headers.get(\"fmodata.affected_rows\");\n if (affectedRows !== null) {\n return { data: parseInt(affectedRows, 10) as T, error: undefined };\n }\n\n // Handle 204 No Content with no body\n if (resp.status === 204) {\n // Check for Location header (used for insert with return=minimal)\n // Use optional chaining for safety with mocks that might not have proper headers\n const locationHeader =\n resp.headers?.get?.(\"Location\") || resp.headers?.get?.(\"location\");\n if (locationHeader) {\n // Return the location header so InsertBuilder can extract ROWID\n return { data: { _location: locationHeader } as T, error: undefined };\n }\n return { data: 0 as T, error: undefined };\n }\n\n // Parse response\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n const data = await safeJsonParse<\n T & { error?: { code?: string | number; message?: string } }\n >(resp);\n\n // Check for embedded OData errors\n if (get(data, \"error\", null)) {\n const errorCode = get(data, \"error.code\", null);\n const errorMessage = get(\n data,\n \"error.message\",\n \"Unknown OData error\",\n );\n\n // Check for schema locked error (code 303)\n if (errorCode === \"303\" || errorCode === 303) {\n return {\n data: undefined,\n error: new SchemaLockedError(fullUrl, errorMessage, data.error),\n };\n }\n\n return {\n data: undefined,\n error: new ODataError(\n fullUrl,\n errorMessage,\n String(errorCode),\n data.error,\n ),\n };\n }\n\n return { data: data as T, error: undefined };\n }\n\n return { data: (await resp.text()) as T, error: undefined };\n } catch (err) {\n // Map ffetch errors - return them directly (no re-wrapping)\n if (\n err instanceof TimeoutError ||\n err instanceof AbortError ||\n err instanceof NetworkError ||\n err instanceof RetryLimitError ||\n err instanceof CircuitOpenError\n ) {\n return { data: undefined, error: err };\n }\n\n // Handle JSON parse errors (ResponseParseError from safeJsonParse)\n if (err instanceof ResponseParseError) {\n return { data: undefined, error: err };\n }\n\n // Unknown error - wrap it as NetworkError\n return {\n data: undefined,\n error: new NetworkError(fullUrl, err),\n };\n }\n }\n\n database<IncludeSpecialColumns extends boolean = false>(\n name: string,\n config?: {\n useEntityIds?: boolean;\n includeSpecialColumns?: IncludeSpecialColumns;\n },\n ): Database<IncludeSpecialColumns> {\n return new Database<IncludeSpecialColumns>(name, this, config);\n }\n\n /**\n * Lists all available databases from the FileMaker OData service.\n * @returns Promise resolving to an array of database names\n */\n async listDatabaseNames(): Promise<string[]> {\n const result = await this._makeRequest<{\n value?: Array<{ name: string }>;\n }>(\"/$metadata\", { headers: { Accept: \"application/json\" } });\n if (result.error) {\n throw result.error;\n }\n if (result.data.value && Array.isArray(result.data.value)) {\n return result.data.value.map((item) => item.name);\n }\n return [];\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;AAqBO,MAAM,mBAA+C;AAAA,EAO1D,YAAY,QAKT;AAXK;AACA;AACA;AACA,wCAAwB;AACxB,iDAAiC;AACjC;AAOD,SAAA,SAAS,aAAa,OAAO,MAAM;AACxC,SAAK,cAAc,aAAa;AAAA,MAC9B,SAAS;AAAA,MACT,GAAG,OAAO;AAAA,IAAA,CACX;AAED,UAAM,MAAM,IAAI,IAAI,OAAO,SAAS;AAChC,QAAA,IAAI,aAAa,UAAU;AAC7B,UAAI,WAAW;AAAA,IAAA;AAGjB,QAAI,WAAW,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAC9C,SAAK,YAAY,IAAI,SAAW,EAAA,QAAQ,QAAQ,EAAE;AAClD,SAAK,OAAO,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,iBAAiB,cAA6B;AAC5C,SAAK,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtB,mBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,0BAA0B,uBAAsC;AAC9D,SAAK,wBAAwB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B,4BAAqC;AACnC,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,cAAsB;AACb,WAAA,GAAG,KAAK,SAAS,GAAG,YAAY,KAAK,OAAO,UAAU,EAAE;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjE,aAA6B;AAC3B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,MAAM,aACJ,KACA,SAKoB;;AACd,UAAA,SAAS,KAAK,WAAW;AACzB,UAAA,UAAU,GAAG,KAAK,SAAS,GAAG,YAAY,KAAK,OAAO,UAAU,EAAE;AACxE,UAAM,UAAU,UAAU;AAGpB,UAAA,gBAAe,mCAAS,iBAAgB,KAAK;AAC7C,UAAA,yBACJ,mCAAS,0BAAyB,KAAK;AAGzC,UAAM,0BAA2B,mCAAiB;AAGlD,UAAM,eAAyB,CAAC;AAChC,QAAI,cAAc;AAChB,mBAAa,KAAK,oBAAoB;AAAA,IAAA;AAExC,QAAI,uBAAuB;AACzB,mBAAa,KAAK,gCAAgC;AAAA,IAAA;AAGpD,UAAM,UAAU;AAAA,MACd,eACE,YAAY,KAAK,OACb,UAAU,KAAK,KAAK,MAAM,KAC1B,SAAS,KAAK,GAAG,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;AAAA,MAClE,gBAAgB;AAAA,MAChB,QAAQ,gBAAgB,uBAAuB;AAAA,MAC/C,GAAI,aAAa,SAAS,IAAI,EAAE,QAAQ,aAAa,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,MACrE,IAAI,mCAAS,YAAW,CAAA;AAAA,IAC1B;AAGA,UAAM,EAAE,eAAe,GAAG,gBAAA,IAAoB;AACvC,WAAA,MAAM,oBAAoB,eAAe;AAIhD,UAAM,eAAe,mCAAS;AACxB,UAAA;AAAA,MACJ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,GAAG;AAAA,IACL,IAAI,WAAW,CAAC;AAIV,UAAA,cAAc,eAChB,aAAa,EAAE,SAAS,GAAG,aAAA,CAAc,IACzC,KAAK;AAEL,QAAA;AACF,YAAM,eAAe;AAAA,QACnB,GAAG;AAAA,QACH;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,YAAY,SAAS,YAAY;AAC7C,aAAA,MAAM,GAAG,aAAa,UAAU,KAAK,IAAI,KAAK,MAAM,IAAI,OAAO,EAAE;AAGpE,UAAA,CAAC,KAAK,IAAI;AAER,YAAA;AAGA,YAAA;AACF,eAAI,UAAK,QAAQ,IAAI,cAAc,MAA/B,mBAAkC,SAAS,qBAAqB;AACtD,wBAAA,MAAM,cAAgC,IAAI;AAAA,UAAA;AAAA,QACxD,QACM;AAAA,QAAA;AAKR,YAAI,uCAAW,OAAO;AACd,gBAAA,YAAY,UAAU,MAAM;AAClC,gBAAM,eAAe,UAAU,MAAM,WAAW,KAAK;AAGjD,cAAA,cAAc,SAAS,cAAc,KAAK;AACrC,mBAAA;AAAA,cACL,MAAM;AAAA,cACN,OAAO,IAAI;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,UAAU;AAAA,cAAA;AAAA,YAEd;AAAA,UAAA;AAGK,iBAAA;AAAA,YACL,MAAM;AAAA,YACN,OAAO,IAAI;AAAA,cACT;AAAA,cACA;AAAA,cACA,OAAO,SAAS;AAAA,cAChB,UAAU;AAAA,YAAA;AAAA,UAEd;AAAA,QAAA;AAGK,eAAA;AAAA,UACL,MAAM;AAAA,UACN,OAAO,IAAI;AAAA,YACT;AAAA,YACA,KAAK;AAAA,YACL,KAAK;AAAA,YACL;AAAA,UAAA;AAAA,QAEJ;AAAA,MAAA;AAKF,YAAM,eAAe,KAAK,QAAQ,IAAI,uBAAuB;AAC7D,UAAI,iBAAiB,MAAM;AACzB,eAAO,EAAE,MAAM,SAAS,cAAc,EAAE,GAAQ,OAAO,OAAU;AAAA,MAAA;AAI/D,UAAA,KAAK,WAAW,KAAK;AAGjB,cAAA,mBACJ,gBAAK,YAAL,mBAAc,QAAd,4BAAoB,kBAAe,gBAAK,YAAL,mBAAc,QAAd,4BAAoB;AACzD,YAAI,gBAAgB;AAElB,iBAAO,EAAE,MAAM,EAAE,WAAW,eAAe,GAAQ,OAAO,OAAU;AAAA,QAAA;AAEtE,eAAO,EAAE,MAAM,GAAQ,OAAO,OAAU;AAAA,MAAA;AAI1C,WAAI,UAAK,QAAQ,IAAI,cAAc,MAA/B,mBAAkC,SAAS,qBAAqB;AAC5D,cAAA,OAAO,MAAM,cAEjB,IAAI;AAGN,YAAI,IAAI,MAAM,SAAS,IAAI,GAAG;AAC5B,gBAAM,YAAY,IAAI,MAAM,cAAc,IAAI;AAC9C,gBAAM,eAAe;AAAA,YACnB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAGI,cAAA,cAAc,SAAS,cAAc,KAAK;AACrC,mBAAA;AAAA,cACL,MAAM;AAAA,cACN,OAAO,IAAI,kBAAkB,SAAS,cAAc,KAAK,KAAK;AAAA,YAChE;AAAA,UAAA;AAGK,iBAAA;AAAA,YACL,MAAM;AAAA,YACN,OAAO,IAAI;AAAA,cACT;AAAA,cACA;AAAA,cACA,OAAO,SAAS;AAAA,cAChB,KAAK;AAAA,YAAA;AAAA,UAET;AAAA,QAAA;AAGK,eAAA,EAAE,MAAiB,OAAO,OAAU;AAAA,MAAA;AAG7C,aAAO,EAAE,MAAO,MAAM,KAAK,KAAK,GAAS,OAAO,OAAU;AAAA,aACnD,KAAK;AAGV,UAAA,eAAe,gBACf,eAAe,cACf,eAAe,gBACf,eAAe,mBACf,eAAe,kBACf;AACA,eAAO,EAAE,MAAM,QAAW,OAAO,IAAI;AAAA,MAAA;AAIvC,UAAI,eAAe,oBAAoB;AACrC,eAAO,EAAE,MAAM,QAAW,OAAO,IAAI;AAAA,MAAA;AAIhC,aAAA;AAAA,QACL,MAAM;AAAA,QACN,OAAO,IAAI,aAAa,SAAS,GAAG;AAAA,MACtC;AAAA,IAAA;AAAA,EACF;AAAA,EAGF,SACE,MACA,QAIiC;AACjC,WAAO,IAAI,SAAgC,MAAM,MAAM,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/D,MAAM,oBAAuC;AACrC,UAAA,SAAS,MAAM,KAAK,aAEvB,cAAc,EAAE,SAAS,EAAE,QAAQ,mBAAmB,GAAG;AAC5D,QAAI,OAAO,OAAO;AAChB,YAAM,OAAO;AAAA,IAAA;AAEX,QAAA,OAAO,KAAK,SAAS,MAAM,QAAQ,OAAO,KAAK,KAAK,GAAG;AACzD,aAAO,OAAO,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,IAAA;AAElD,WAAO,CAAC;AAAA,EAAA;AAEZ;"}
@@ -1,23 +1,53 @@
1
- import { ExecutionContext, ExecutableBuilder, Result, ODataRecordMetadata } from '../types.js';
2
- import { TableOccurrence } from './table-occurrence.js';
3
- import { FFetchOptions } from '@fetchkit/ffetch';
4
- export declare class InsertBuilder<T extends Record<string, any>, Occ extends TableOccurrence<any, any, any, any> | undefined = undefined> implements ExecutableBuilder<T & ODataRecordMetadata> {
5
- private occurrence?;
6
- private tableName;
1
+ import { ExecutionContext, ExecutableBuilder, Result, ExecuteOptions, ConditionallyWithODataAnnotations, ExecuteMethodOptions } from '../types.js';
2
+ import { FMTable, InferSchemaOutputFromFMTable } from '../orm/table.js';
3
+ export type InsertOptions = {
4
+ return?: "minimal" | "representation";
5
+ };
6
+ export declare class InsertBuilder<Occ extends FMTable<any, any> | undefined = undefined, ReturnPreference extends "minimal" | "representation" = "representation"> implements ExecutableBuilder<ReturnPreference extends "minimal" ? {
7
+ ROWID: number;
8
+ } : InferSchemaOutputFromFMTable<NonNullable<Occ>>> {
9
+ private table?;
7
10
  private databaseName;
8
11
  private context;
9
12
  private data;
13
+ private returnPreference;
14
+ private databaseUseEntityIds;
15
+ private databaseIncludeSpecialColumns;
10
16
  constructor(config: {
11
17
  occurrence?: Occ;
12
- tableName: string;
13
18
  databaseName: string;
14
19
  context: ExecutionContext;
15
- data: Partial<T>;
20
+ data: Partial<InferSchemaOutputFromFMTable<NonNullable<Occ>>>;
21
+ returnPreference?: ReturnPreference;
22
+ databaseUseEntityIds?: boolean;
23
+ databaseIncludeSpecialColumns?: boolean;
16
24
  });
17
- execute(options?: RequestInit & FFetchOptions): Promise<Result<T & ODataRecordMetadata>>;
25
+ /**
26
+ * Helper to merge database-level useEntityIds with per-request options
27
+ */
28
+ private mergeExecuteOptions;
29
+ /**
30
+ * Parse ROWID from Location header
31
+ * Expected formats:
32
+ * - contacts(ROWID=4583)
33
+ * - contacts('some-uuid')
34
+ */
35
+ private parseLocationHeader;
36
+ /**
37
+ * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
38
+ * @param useEntityIds - Optional override for entity ID usage
39
+ */
40
+ private getTableId;
41
+ execute<EO extends ExecuteOptions>(options?: ExecuteMethodOptions<EO>): Promise<Result<ReturnPreference extends "minimal" ? {
42
+ ROWID: number;
43
+ } : ConditionallyWithODataAnnotations<InferSchemaOutputFromFMTable<NonNullable<Occ>>, EO["includeODataAnnotations"] extends true ? true : false>>>;
18
44
  getRequestConfig(): {
19
45
  method: string;
20
46
  url: string;
21
47
  body?: any;
22
48
  };
49
+ toRequest(baseUrl: string, options?: ExecuteOptions): Request;
50
+ processResponse(response: Response, options?: ExecuteOptions): Promise<Result<ReturnPreference extends "minimal" ? {
51
+ ROWID: number;
52
+ } : InferSchemaOutputFromFMTable<NonNullable<Occ>>>>;
23
53
  }