@proofkit/fmodata 0.1.0-alpha.0 → 0.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1624 -18
- package/dist/esm/client/base-table.d.ts +117 -5
- package/dist/esm/client/base-table.js +43 -5
- package/dist/esm/client/base-table.js.map +1 -1
- package/dist/esm/client/batch-builder.d.ts +54 -0
- package/dist/esm/client/batch-builder.js +179 -0
- package/dist/esm/client/batch-builder.js.map +1 -0
- package/dist/esm/client/batch-request.d.ts +61 -0
- package/dist/esm/client/batch-request.js +252 -0
- package/dist/esm/client/batch-request.js.map +1 -0
- package/dist/esm/client/database.d.ts +55 -6
- package/dist/esm/client/database.js +118 -15
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +21 -2
- package/dist/esm/client/delete-builder.js +96 -32
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +25 -11
- package/dist/esm/client/entity-set.js +31 -11
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/filemaker-odata.d.ts +23 -4
- package/dist/esm/client/filemaker-odata.js +124 -29
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +38 -3
- package/dist/esm/client/insert-builder.js +231 -34
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query-builder.d.ts +27 -6
- package/dist/esm/client/query-builder.js +457 -210
- package/dist/esm/client/query-builder.js.map +1 -1
- package/dist/esm/client/record-builder.d.ts +96 -9
- package/dist/esm/client/record-builder.js +378 -39
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/response-processor.d.ts +38 -0
- package/dist/esm/client/schema-manager.d.ts +57 -0
- package/dist/esm/client/schema-manager.js +132 -0
- package/dist/esm/client/schema-manager.js.map +1 -0
- package/dist/esm/client/table-occurrence.d.ts +48 -1
- package/dist/esm/client/table-occurrence.js +29 -2
- package/dist/esm/client/table-occurrence.js.map +1 -1
- package/dist/esm/client/update-builder.d.ts +34 -11
- package/dist/esm/client/update-builder.js +135 -31
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/errors.d.ts +73 -0
- package/dist/esm/errors.js +148 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +10 -3
- package/dist/esm/index.js +28 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/transform.d.ts +65 -0
- package/dist/esm/transform.js +114 -0
- package/dist/esm/transform.js.map +1 -0
- package/dist/esm/types.d.ts +89 -5
- package/dist/esm/validation.d.ts +6 -3
- package/dist/esm/validation.js +104 -33
- package/dist/esm/validation.js.map +1 -1
- package/package.json +10 -1
- package/src/client/base-table.ts +158 -8
- package/src/client/batch-builder.ts +265 -0
- package/src/client/batch-request.ts +485 -0
- package/src/client/database.ts +175 -18
- package/src/client/delete-builder.ts +149 -48
- package/src/client/entity-set.ts +114 -23
- package/src/client/filemaker-odata.ts +179 -35
- package/src/client/insert-builder.ts +350 -40
- package/src/client/query-builder.ts +616 -237
- package/src/client/query-builder.ts.bak +1457 -0
- package/src/client/record-builder.ts +692 -65
- package/src/client/response-processor.ts +103 -0
- package/src/client/schema-manager.ts +246 -0
- package/src/client/table-occurrence.ts +78 -3
- package/src/client/update-builder.ts +235 -49
- package/src/errors.ts +217 -0
- package/src/index.ts +59 -2
- package/src/transform.ts +249 -0
- package/src/types.ts +201 -35
- package/src/validation.ts +120 -36
|
@@ -1,13 +1,15 @@
|
|
|
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 { SchemaLockedError, ODataError, HTTPError } from "../errors.js";
|
|
5
6
|
import { Database } from "./database.js";
|
|
6
|
-
class
|
|
7
|
+
class FMServerConnection {
|
|
7
8
|
constructor(config) {
|
|
8
9
|
__publicField(this, "fetchClient");
|
|
9
10
|
__publicField(this, "serverUrl");
|
|
10
11
|
__publicField(this, "auth");
|
|
12
|
+
__publicField(this, "useEntityIds", false);
|
|
11
13
|
this.fetchClient = createClient({
|
|
12
14
|
retries: 0,
|
|
13
15
|
...config.fetchClientOptions
|
|
@@ -20,16 +22,40 @@ class FileMakerOData {
|
|
|
20
22
|
this.serverUrl = url.toString().replace(/\/+$/, "");
|
|
21
23
|
this.auth = config.auth;
|
|
22
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
* Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
|
|
28
|
+
*/
|
|
29
|
+
_setUseEntityIds(useEntityIds) {
|
|
30
|
+
this.useEntityIds = useEntityIds;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* @internal
|
|
34
|
+
* Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
|
|
35
|
+
*/
|
|
36
|
+
_getUseEntityIds() {
|
|
37
|
+
return this.useEntityIds;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @internal
|
|
41
|
+
* Gets the base URL for OData requests
|
|
42
|
+
*/
|
|
43
|
+
_getBaseUrl() {
|
|
44
|
+
return `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
|
|
45
|
+
}
|
|
23
46
|
/**
|
|
24
47
|
* @internal
|
|
25
48
|
*/
|
|
26
49
|
async _makeRequest(url, options) {
|
|
27
|
-
var _a;
|
|
50
|
+
var _a, _b, _c, _d, _e, _f;
|
|
28
51
|
const baseUrl = `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
|
|
52
|
+
const fullUrl = baseUrl + url;
|
|
53
|
+
const useEntityIds = (options == null ? void 0 : options.useEntityIds) ?? this.useEntityIds;
|
|
29
54
|
const headers = {
|
|
30
55
|
Authorization: "apiKey" in this.auth ? `Bearer ${this.auth.apiKey}` : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,
|
|
31
56
|
"Content-Type": "application/json",
|
|
32
57
|
Accept: "application/json",
|
|
58
|
+
...useEntityIds ? { Prefer: "fmodata.entity-ids" } : {},
|
|
33
59
|
...(options == null ? void 0 : options.headers) || {}
|
|
34
60
|
};
|
|
35
61
|
const fetchHandler = options == null ? void 0 : options.fetchHandler;
|
|
@@ -39,30 +65,96 @@ class FileMakerOData {
|
|
|
39
65
|
...restOptions
|
|
40
66
|
} = options || {};
|
|
41
67
|
const clientToUse = fetchHandler ? createClient({ retries: 0, fetchHandler }) : this.fetchClient;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
try {
|
|
69
|
+
const finalOptions = {
|
|
70
|
+
...restOptions,
|
|
71
|
+
headers
|
|
72
|
+
};
|
|
73
|
+
const resp = url.includes("/$batch") ? await fetch(fullUrl, {
|
|
74
|
+
method: finalOptions.method,
|
|
75
|
+
headers: finalOptions.headers,
|
|
76
|
+
body: finalOptions.body
|
|
77
|
+
}) : await clientToUse(fullUrl, finalOptions);
|
|
78
|
+
if (!resp.ok) {
|
|
79
|
+
let errorBody;
|
|
80
|
+
try {
|
|
81
|
+
if ((_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
|
|
82
|
+
errorBody = await resp.json();
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
if (errorBody == null ? void 0 : errorBody.error) {
|
|
87
|
+
const errorCode = errorBody.error.code;
|
|
88
|
+
const errorMessage = errorBody.error.message || resp.statusText;
|
|
89
|
+
if (errorCode === "303" || errorCode === 303) {
|
|
90
|
+
return {
|
|
91
|
+
data: void 0,
|
|
92
|
+
error: new SchemaLockedError(
|
|
93
|
+
fullUrl,
|
|
94
|
+
errorMessage,
|
|
95
|
+
errorBody.error
|
|
96
|
+
)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
data: void 0,
|
|
101
|
+
error: new ODataError(
|
|
102
|
+
fullUrl,
|
|
103
|
+
errorMessage,
|
|
104
|
+
errorCode,
|
|
105
|
+
errorBody.error
|
|
106
|
+
)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
data: void 0,
|
|
111
|
+
error: new HTTPError(
|
|
112
|
+
fullUrl,
|
|
113
|
+
resp.status,
|
|
114
|
+
resp.statusText,
|
|
115
|
+
errorBody
|
|
116
|
+
)
|
|
117
|
+
};
|
|
62
118
|
}
|
|
63
|
-
|
|
119
|
+
const affectedRows = resp.headers.get("fmodata.affected_rows");
|
|
120
|
+
if (affectedRows !== null) {
|
|
121
|
+
return { data: parseInt(affectedRows, 10), error: void 0 };
|
|
122
|
+
}
|
|
123
|
+
if (resp.status === 204) {
|
|
124
|
+
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"));
|
|
125
|
+
if (locationHeader) {
|
|
126
|
+
return { data: { _location: locationHeader }, error: void 0 };
|
|
127
|
+
}
|
|
128
|
+
return { data: 0, error: void 0 };
|
|
129
|
+
}
|
|
130
|
+
if ((_f = resp.headers.get("content-type")) == null ? void 0 : _f.includes("application/json")) {
|
|
131
|
+
const data = await resp.json();
|
|
132
|
+
if (data.error) {
|
|
133
|
+
const errorCode = data.error.code;
|
|
134
|
+
const errorMessage = data.error.message || "Unknown OData error";
|
|
135
|
+
if (errorCode === "303" || errorCode === 303) {
|
|
136
|
+
return {
|
|
137
|
+
data: void 0,
|
|
138
|
+
error: new SchemaLockedError(fullUrl, errorMessage, data.error)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
data: void 0,
|
|
143
|
+
error: new ODataError(fullUrl, errorMessage, errorCode, data.error)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { data, error: void 0 };
|
|
147
|
+
}
|
|
148
|
+
return { data: await resp.text(), error: void 0 };
|
|
149
|
+
} catch (err) {
|
|
150
|
+
if (err instanceof TimeoutError || err instanceof AbortError || err instanceof NetworkError || err instanceof RetryLimitError || err instanceof CircuitOpenError) {
|
|
151
|
+
return { data: void 0, error: err };
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
data: void 0,
|
|
155
|
+
error: new NetworkError(fullUrl, err)
|
|
156
|
+
};
|
|
64
157
|
}
|
|
65
|
-
return await resp.text();
|
|
66
158
|
}
|
|
67
159
|
database(name, config) {
|
|
68
160
|
return new Database(name, this, config);
|
|
@@ -72,14 +164,17 @@ class FileMakerOData {
|
|
|
72
164
|
* @returns Promise resolving to an array of database names
|
|
73
165
|
*/
|
|
74
166
|
async listDatabaseNames() {
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
|
|
167
|
+
const result = await this._makeRequest("/");
|
|
168
|
+
if (result.error) {
|
|
169
|
+
throw result.error;
|
|
170
|
+
}
|
|
171
|
+
if (result.data.value && Array.isArray(result.data.value)) {
|
|
172
|
+
return result.data.value.map((item) => item.name);
|
|
78
173
|
}
|
|
79
174
|
return [];
|
|
80
175
|
}
|
|
81
176
|
}
|
|
82
177
|
export {
|
|
83
|
-
|
|
178
|
+
FMServerConnection
|
|
84
179
|
};
|
|
85
180
|
//# sourceMappingURL=filemaker-odata.js.map
|
|
@@ -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 FileMakerOData 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,eAA2C;AAAA,EAItD,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 { HTTPError, ODataError, SchemaLockedError } from \"../errors\";\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 private useEntityIds: boolean = false;\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 * 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 * 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 */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<Result<T>> {\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\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 ...(useEntityIds ? { Prefer: \"fmodata.entity-ids\" } : {}),\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 try {\n const finalOptions = {\n ...restOptions,\n headers,\n };\n\n // For batch requests, use native fetch to avoid any potential serialization issues with ffetch\n const resp = url.includes(\"/$batch\")\n ? await fetch(fullUrl, {\n method: finalOptions.method,\n headers: finalOptions.headers,\n body: finalOptions.body,\n })\n : await clientToUse(fullUrl, finalOptions);\n\n // Handle HTTP errors\n if (!resp.ok) {\n // Try to parse error body if it's JSON\n let errorBody;\n try {\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n errorBody = await resp.json();\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 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 resp.json();\n\n // Check for embedded OData errors\n if (data.error) {\n const errorCode = data.error.code;\n const errorMessage = data.error.message || \"Unknown OData error\";\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(fullUrl, errorMessage, errorCode, data.error),\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 // Unknown error - wrap it as NetworkError\n return {\n data: undefined,\n error: new NetworkError(fullUrl, err),\n };\n }\n }\n\n database<\n const Occurrences extends readonly TableOccurrence<any, any, any, any>[],\n >(\n name: string,\n config?: {\n occurrences?: Occurrences | undefined;\n useEntityIds?: boolean;\n },\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 result = await this._makeRequest<{\n value?: Array<{ name: string }>;\n }>(\"/\");\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":"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"}
|
|
@@ -1,23 +1,58 @@
|
|
|
1
|
-
import { ExecutionContext, ExecutableBuilder, Result,
|
|
1
|
+
import { ExecutionContext, ExecutableBuilder, Result, ExecuteOptions, ConditionallyWithODataAnnotations } from '../types.js';
|
|
2
2
|
import { TableOccurrence } from './table-occurrence.js';
|
|
3
3
|
import { FFetchOptions } from '@fetchkit/ffetch';
|
|
4
|
-
export
|
|
4
|
+
export type InsertOptions = {
|
|
5
|
+
return?: "minimal" | "representation";
|
|
6
|
+
};
|
|
7
|
+
export declare class InsertBuilder<T extends Record<string, any>, Occ extends TableOccurrence<any, any, any, any> | undefined = undefined, ReturnPreference extends "minimal" | "representation" = "representation"> implements ExecutableBuilder<ReturnPreference extends "minimal" ? {
|
|
8
|
+
ROWID: number;
|
|
9
|
+
} : T> {
|
|
5
10
|
private occurrence?;
|
|
6
11
|
private tableName;
|
|
7
12
|
private databaseName;
|
|
8
13
|
private context;
|
|
9
14
|
private data;
|
|
15
|
+
private returnPreference;
|
|
16
|
+
private databaseUseEntityIds;
|
|
10
17
|
constructor(config: {
|
|
11
18
|
occurrence?: Occ;
|
|
12
19
|
tableName: string;
|
|
13
20
|
databaseName: string;
|
|
14
21
|
context: ExecutionContext;
|
|
15
22
|
data: Partial<T>;
|
|
23
|
+
returnPreference?: ReturnPreference;
|
|
24
|
+
databaseUseEntityIds?: boolean;
|
|
16
25
|
});
|
|
17
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Helper to merge database-level useEntityIds with per-request options
|
|
28
|
+
*/
|
|
29
|
+
private mergeExecuteOptions;
|
|
30
|
+
/**
|
|
31
|
+
* Helper to conditionally strip OData annotations based on options
|
|
32
|
+
*/
|
|
33
|
+
private stripODataAnnotationsIfNeeded;
|
|
34
|
+
/**
|
|
35
|
+
* Parse ROWID from Location header
|
|
36
|
+
* Expected formats:
|
|
37
|
+
* - contacts(ROWID=4583)
|
|
38
|
+
* - contacts('some-uuid')
|
|
39
|
+
*/
|
|
40
|
+
private parseLocationHeader;
|
|
41
|
+
/**
|
|
42
|
+
* Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
|
|
43
|
+
* @param useEntityIds - Optional override for entity ID usage
|
|
44
|
+
*/
|
|
45
|
+
private getTableId;
|
|
46
|
+
execute<EO extends ExecuteOptions>(options?: RequestInit & FFetchOptions & EO): Promise<Result<ReturnPreference extends "minimal" ? {
|
|
47
|
+
ROWID: number;
|
|
48
|
+
} : ConditionallyWithODataAnnotations<T, EO["includeODataAnnotations"] extends true ? true : false>>>;
|
|
18
49
|
getRequestConfig(): {
|
|
19
50
|
method: string;
|
|
20
51
|
url: string;
|
|
21
52
|
body?: any;
|
|
22
53
|
};
|
|
54
|
+
toRequest(baseUrl: string): Request;
|
|
55
|
+
processResponse(response: Response, options?: ExecuteOptions): Promise<Result<ReturnPreference extends "minimal" ? {
|
|
56
|
+
ROWID: number;
|
|
57
|
+
} : T>>;
|
|
23
58
|
}
|
|
@@ -2,6 +2,8 @@ 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
4
|
import { validateSingleResponse } from "../validation.js";
|
|
5
|
+
import { getTableIdentifiers, transformFieldNamesToIds, transformResponseFields } from "../transform.js";
|
|
6
|
+
import { InvalidLocationHeaderError } from "../errors.js";
|
|
5
7
|
class InsertBuilder {
|
|
6
8
|
constructor(config) {
|
|
7
9
|
__publicField(this, "occurrence");
|
|
@@ -9,59 +11,254 @@ class InsertBuilder {
|
|
|
9
11
|
__publicField(this, "databaseName");
|
|
10
12
|
__publicField(this, "context");
|
|
11
13
|
__publicField(this, "data");
|
|
14
|
+
__publicField(this, "returnPreference");
|
|
15
|
+
__publicField(this, "databaseUseEntityIds");
|
|
12
16
|
this.occurrence = config.occurrence;
|
|
13
17
|
this.tableName = config.tableName;
|
|
14
18
|
this.databaseName = config.databaseName;
|
|
15
19
|
this.context = config.context;
|
|
16
20
|
this.data = config.data;
|
|
21
|
+
this.returnPreference = config.returnPreference || "representation";
|
|
22
|
+
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
17
23
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Helper to merge database-level useEntityIds with per-request options
|
|
26
|
+
*/
|
|
27
|
+
mergeExecuteOptions(options) {
|
|
28
|
+
return {
|
|
29
|
+
...options,
|
|
30
|
+
useEntityIds: (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Helper to conditionally strip OData annotations based on options
|
|
35
|
+
*/
|
|
36
|
+
stripODataAnnotationsIfNeeded(data, options) {
|
|
37
|
+
if ((options == null ? void 0 : options.includeODataAnnotations) === true) {
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
const { "@id": _id, "@editLink": _editLink, ...rest } = data;
|
|
41
|
+
return rest;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse ROWID from Location header
|
|
45
|
+
* Expected formats:
|
|
46
|
+
* - contacts(ROWID=4583)
|
|
47
|
+
* - contacts('some-uuid')
|
|
48
|
+
*/
|
|
49
|
+
parseLocationHeader(locationHeader) {
|
|
50
|
+
if (!locationHeader) {
|
|
51
|
+
throw new InvalidLocationHeaderError(
|
|
52
|
+
"Location header is required but was not provided"
|
|
40
53
|
);
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
}
|
|
55
|
+
const rowidMatch = locationHeader.match(/ROWID=(\d+)/);
|
|
56
|
+
if (rowidMatch && rowidMatch[1]) {
|
|
57
|
+
return parseInt(rowidMatch[1], 10);
|
|
58
|
+
}
|
|
59
|
+
const parenMatch = locationHeader.match(/\(['"]?([^'"]+)['"]?\)/);
|
|
60
|
+
if (parenMatch && parenMatch[1]) {
|
|
61
|
+
const value = parenMatch[1];
|
|
62
|
+
const numValue = parseInt(value, 10);
|
|
63
|
+
if (!isNaN(numValue)) {
|
|
64
|
+
return numValue;
|
|
43
65
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
}
|
|
67
|
+
throw new InvalidLocationHeaderError(
|
|
68
|
+
`Could not extract ROWID from Location header: ${locationHeader}`,
|
|
69
|
+
locationHeader
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
|
|
74
|
+
* @param useEntityIds - Optional override for entity ID usage
|
|
75
|
+
*/
|
|
76
|
+
getTableId(useEntityIds) {
|
|
77
|
+
var _a, _b;
|
|
78
|
+
if (!this.occurrence) {
|
|
79
|
+
return this.tableName;
|
|
80
|
+
}
|
|
81
|
+
const contextDefault = ((_b = (_a = this.context)._getUseEntityIds) == null ? void 0 : _b.call(_a)) ?? false;
|
|
82
|
+
const shouldUseIds = useEntityIds ?? contextDefault;
|
|
83
|
+
if (shouldUseIds) {
|
|
84
|
+
const identifiers = getTableIdentifiers(this.occurrence);
|
|
85
|
+
if (!identifiers.id) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`useEntityIds is true but TableOccurrence "${identifiers.name}" does not have an fmtId defined`
|
|
88
|
+
);
|
|
49
89
|
}
|
|
50
|
-
return
|
|
51
|
-
}
|
|
90
|
+
return identifiers.id;
|
|
91
|
+
}
|
|
92
|
+
return this.occurrence.getTableName();
|
|
93
|
+
}
|
|
94
|
+
async execute(options) {
|
|
95
|
+
var _a, _b, _c, _d;
|
|
96
|
+
const mergedOptions = this.mergeExecuteOptions(options);
|
|
97
|
+
const tableId = this.getTableId(mergedOptions.useEntityIds);
|
|
98
|
+
const url = `/${this.databaseName}/${tableId}`;
|
|
99
|
+
const shouldUseIds = mergedOptions.useEntityIds ?? false;
|
|
100
|
+
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) && shouldUseIds ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
101
|
+
const preferHeader = this.returnPreference === "minimal" ? "return=minimal" : "return=representation";
|
|
102
|
+
const result = await this.context._makeRequest(url, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
Prefer: preferHeader,
|
|
107
|
+
...(mergedOptions == null ? void 0 : mergedOptions.headers) || {}
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify(transformedData),
|
|
110
|
+
...mergedOptions
|
|
111
|
+
});
|
|
112
|
+
if (result.error) {
|
|
113
|
+
return { data: void 0, error: result.error };
|
|
114
|
+
}
|
|
115
|
+
if (this.returnPreference === "minimal") {
|
|
116
|
+
const responseData = result.data;
|
|
117
|
+
if (!responseData || !responseData._location) {
|
|
118
|
+
throw new InvalidLocationHeaderError(
|
|
119
|
+
"Location header is required when using return=minimal but was not found in response"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const rowid = this.parseLocationHeader(responseData._location);
|
|
123
|
+
return { data: { ROWID: rowid }, error: void 0 };
|
|
124
|
+
}
|
|
125
|
+
let response = result.data;
|
|
126
|
+
if (((_b = this.occurrence) == null ? void 0 : _b.baseTable) && shouldUseIds) {
|
|
127
|
+
response = transformResponseFields(
|
|
128
|
+
response,
|
|
129
|
+
this.occurrence.baseTable,
|
|
130
|
+
void 0
|
|
131
|
+
// No expand configs for insert
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const schema = (_d = (_c = this.occurrence) == null ? void 0 : _c.baseTable) == null ? void 0 : _d.schema;
|
|
135
|
+
const validation = await validateSingleResponse(
|
|
136
|
+
response,
|
|
137
|
+
schema,
|
|
138
|
+
void 0,
|
|
139
|
+
// No selected fields for insert
|
|
140
|
+
void 0,
|
|
141
|
+
// No expand configs
|
|
142
|
+
"exact"
|
|
143
|
+
// Expect exactly one record
|
|
144
|
+
);
|
|
145
|
+
if (!validation.valid) {
|
|
146
|
+
return { data: void 0, error: validation.error };
|
|
147
|
+
}
|
|
148
|
+
if (validation.data === null) {
|
|
52
149
|
return {
|
|
53
150
|
data: void 0,
|
|
54
|
-
error:
|
|
151
|
+
error: new Error("Insert operation returned null response")
|
|
55
152
|
};
|
|
56
153
|
}
|
|
154
|
+
const finalData = this.stripODataAnnotationsIfNeeded(
|
|
155
|
+
validation.data,
|
|
156
|
+
options
|
|
157
|
+
);
|
|
158
|
+
return { data: finalData, error: void 0 };
|
|
57
159
|
}
|
|
58
160
|
getRequestConfig() {
|
|
161
|
+
var _a;
|
|
162
|
+
const tableId = this.getTableId(this.databaseUseEntityIds);
|
|
163
|
+
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) && this.databaseUseEntityIds ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
59
164
|
return {
|
|
60
165
|
method: "POST",
|
|
61
|
-
url: `/${this.databaseName}/${
|
|
62
|
-
body: JSON.stringify(
|
|
166
|
+
url: `/${this.databaseName}/${tableId}`,
|
|
167
|
+
body: JSON.stringify(transformedData)
|
|
63
168
|
};
|
|
64
169
|
}
|
|
170
|
+
toRequest(baseUrl) {
|
|
171
|
+
const config = this.getRequestConfig();
|
|
172
|
+
const fullUrl = `${baseUrl}${config.url}`;
|
|
173
|
+
const preferHeader = this.returnPreference === "minimal" ? "return=minimal" : "return=representation";
|
|
174
|
+
return new Request(fullUrl, {
|
|
175
|
+
method: config.method,
|
|
176
|
+
headers: {
|
|
177
|
+
"Content-Type": "application/json",
|
|
178
|
+
Accept: "application/json",
|
|
179
|
+
Prefer: preferHeader
|
|
180
|
+
},
|
|
181
|
+
body: config.body
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async processResponse(response, options) {
|
|
185
|
+
var _a, _b, _c;
|
|
186
|
+
if (response.status === 204) {
|
|
187
|
+
if (this.returnPreference === "minimal") {
|
|
188
|
+
const locationHeader = response.headers.get("Location") || response.headers.get("location");
|
|
189
|
+
if (locationHeader) {
|
|
190
|
+
const rowid = this.parseLocationHeader(locationHeader);
|
|
191
|
+
return { data: { ROWID: rowid }, error: void 0 };
|
|
192
|
+
}
|
|
193
|
+
throw new InvalidLocationHeaderError(
|
|
194
|
+
"Location header is required when using return=minimal but was not found in response"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
data: {},
|
|
199
|
+
error: void 0
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (this.returnPreference === "minimal") {
|
|
203
|
+
throw new InvalidLocationHeaderError(
|
|
204
|
+
"Expected 204 No Content for return=minimal, but received response with body"
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
let rawResponse;
|
|
208
|
+
try {
|
|
209
|
+
rawResponse = await response.json();
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (response.status === 204) {
|
|
212
|
+
return {
|
|
213
|
+
data: {},
|
|
214
|
+
error: void 0
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
data: void 0,
|
|
219
|
+
error: {
|
|
220
|
+
name: "ResponseParseError",
|
|
221
|
+
message: `Failed to parse response JSON: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
222
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const shouldUseIds = (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds;
|
|
227
|
+
let transformedResponse = rawResponse;
|
|
228
|
+
if (((_a = this.occurrence) == null ? void 0 : _a.baseTable) && shouldUseIds) {
|
|
229
|
+
transformedResponse = transformResponseFields(
|
|
230
|
+
rawResponse,
|
|
231
|
+
this.occurrence.baseTable,
|
|
232
|
+
void 0
|
|
233
|
+
// No expand configs for insert
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const schema = (_c = (_b = this.occurrence) == null ? void 0 : _b.baseTable) == null ? void 0 : _c.schema;
|
|
237
|
+
const validation = await validateSingleResponse(
|
|
238
|
+
transformedResponse,
|
|
239
|
+
schema,
|
|
240
|
+
void 0,
|
|
241
|
+
// No selected fields for insert
|
|
242
|
+
void 0,
|
|
243
|
+
// No expand configs
|
|
244
|
+
"exact"
|
|
245
|
+
// Expect exactly one record
|
|
246
|
+
);
|
|
247
|
+
if (!validation.valid) {
|
|
248
|
+
return { data: void 0, error: validation.error };
|
|
249
|
+
}
|
|
250
|
+
if (validation.data === null) {
|
|
251
|
+
return {
|
|
252
|
+
data: void 0,
|
|
253
|
+
error: new Error("Insert operation returned null response")
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const finalData = this.stripODataAnnotationsIfNeeded(
|
|
257
|
+
validation.data,
|
|
258
|
+
options
|
|
259
|
+
);
|
|
260
|
+
return { data: finalData, error: void 0 };
|
|
261
|
+
}
|
|
65
262
|
}
|
|
66
263
|
export {
|
|
67
264
|
InsertBuilder
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insert-builder.js","sources":["../../../src/client/insert-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n ODataRecordMetadata,\n InferSchemaType,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { validateSingleResponse } from \"../validation\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\n\nexport class InsertBuilder<\n T extends Record<string, any>,\n Occ extends TableOccurrence<any, any, any, any> | undefined = undefined,\n> implements ExecutableBuilder<T & ODataRecordMetadata>\n{\n private occurrence?: Occ;\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private data: Partial<T>;\n\n constructor(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n }\n\n async execute(\n options?: RequestInit & FFetchOptions,\n ): Promise<Result<T & ODataRecordMetadata>> {\n try {\n const url = `/${this.databaseName}/${this.tableName}`;\n\n // Make POST request with JSON body\n const response = await this.context._makeRequest(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(this.data),\n ...options,\n });\n\n // Get schema from occurrence if available\n const schema = this.occurrence?.baseTable?.schema;\n\n // Validate the response (FileMaker returns the created record)\n const validation = await validateSingleResponse<T>(\n response,\n schema,\n undefined, // No selected fields for insert\n undefined, // No expand configs\n \"exact\", // Expect exactly one record\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Handle null response (shouldn't happen for insert, but handle it)\n if (validation.data === null) {\n return {\n data: undefined,\n error: new Error(\"Insert operation returned null response\"),\n };\n }\n\n return { data: validation.data, error: undefined };\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n return {\n method: \"POST\",\n url: `/${this.databaseName}/${this.tableName}`,\n body: JSON.stringify(this.data),\n };\n }\n}\n"],"names":[],"mappings":";;;;AAWO,MAAM,cAIb;AAAA,EAOE,YAAY,QAMT;AAZK;AACA;AACA;AACA;AACA;AASN,SAAK,aAAa,OAAO;AACzB,SAAK,YAAY,OAAO;AACxB,SAAK,eAAe,OAAO;AAC3B,SAAK,UAAU,OAAO;AACtB,SAAK,OAAO,OAAO;AAAA,EAAA;AAAA,EAGrB,MAAM,QACJ,SAC0C;;AACtC,QAAA;AACF,YAAM,MAAM,IAAI,KAAK,YAAY,IAAI,KAAK,SAAS;AAGnD,YAAM,WAAW,MAAM,KAAK,QAAQ,aAAa,KAAK;AAAA,QACpD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,QAC9B,GAAG;AAAA,MAAA,CACJ;AAGK,YAAA,UAAS,gBAAK,eAAL,mBAAiB,cAAjB,mBAA4B;AAG3C,YAAM,aAAa,MAAM;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,MACF;AAEI,UAAA,CAAC,WAAW,OAAO;AACrB,eAAO,EAAE,MAAM,QAAW,OAAO,WAAW,MAAM;AAAA,MAAA;AAIhD,UAAA,WAAW,SAAS,MAAM;AACrB,eAAA;AAAA,UACL,MAAM;AAAA,UACN,OAAO,IAAI,MAAM,yCAAyC;AAAA,QAC5D;AAAA,MAAA;AAGF,aAAO,EAAE,MAAM,WAAW,MAAM,OAAO,OAAU;AAAA,aAC1C,OAAO;AACP,aAAA;AAAA,QACL,MAAM;AAAA,QACN,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IAAA;AAAA,EACF;AAAA,EAGF,mBAAgE;AACvD,WAAA;AAAA,MACL,QAAQ;AAAA,MACR,KAAK,IAAI,KAAK,YAAY,IAAI,KAAK,SAAS;AAAA,MAC5C,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,IAChC;AAAA,EAAA;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"insert-builder.js","sources":["../../../src/client/insert-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n ODataRecordMetadata,\n InferSchemaType,\n ExecuteOptions,\n ConditionallyWithODataAnnotations,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { validateSingleResponse } from \"../validation\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport {\n transformFieldNamesToIds,\n transformTableName,\n transformResponseFields,\n getTableIdentifiers,\n} from \"../transform\";\nimport { InvalidLocationHeaderError } from \"../errors\";\n\nexport type InsertOptions = {\n return?: \"minimal\" | \"representation\";\n};\n\nexport class InsertBuilder<\n T extends Record<string, any>,\n Occ extends TableOccurrence<any, any, any, any> | undefined = undefined,\n ReturnPreference extends \"minimal\" | \"representation\" = \"representation\",\n> implements\n ExecutableBuilder<\n ReturnPreference extends \"minimal\" ? { ROWID: number } : T\n >\n{\n private occurrence?: Occ;\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private data: Partial<T>;\n private returnPreference: ReturnPreference;\n\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n returnPreference?: ReturnPreference;\n databaseUseEntityIds?: boolean;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.returnPreference = (config.returnPreference ||\n \"representation\") as ReturnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Helper to merge database-level useEntityIds with per-request options\n */\n private mergeExecuteOptions(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n // If useEntityIds is not set in options, use the database-level setting\n return {\n ...options,\n useEntityIds: options?.useEntityIds ?? this.databaseUseEntityIds,\n };\n }\n\n /**\n * Helper to conditionally strip OData annotations based on options\n */\n private stripODataAnnotationsIfNeeded<T extends Record<string, any>>(\n data: T,\n options?: ExecuteOptions,\n ): T {\n // Only include annotations if explicitly requested\n if (options?.includeODataAnnotations === true) {\n return data;\n }\n\n // Strip OData annotations\n const { \"@id\": _id, \"@editLink\": _editLink, ...rest } = data;\n return rest as T;\n }\n\n /**\n * Parse ROWID from Location header\n * Expected formats:\n * - contacts(ROWID=4583)\n * - contacts('some-uuid')\n */\n private parseLocationHeader(locationHeader: string | undefined): number {\n if (!locationHeader) {\n throw new InvalidLocationHeaderError(\n \"Location header is required but was not provided\",\n );\n }\n\n // Try to match ROWID=number pattern\n const rowidMatch = locationHeader.match(/ROWID=(\\d+)/);\n if (rowidMatch && rowidMatch[1]) {\n return parseInt(rowidMatch[1], 10);\n }\n\n // Try to extract value from parentheses and parse as number\n const parenMatch = locationHeader.match(/\\(['\"]?([^'\"]+)['\"]?\\)/);\n if (parenMatch && parenMatch[1]) {\n const value = parenMatch[1];\n const numValue = parseInt(value, 10);\n if (!isNaN(numValue)) {\n return numValue;\n }\n }\n\n throw new InvalidLocationHeaderError(\n `Could not extract ROWID from Location header: ${locationHeader}`,\n locationHeader,\n );\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n if (!this.occurrence) {\n return this.tableName;\n }\n\n const contextDefault = this.context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIds ?? contextDefault;\n\n if (shouldUseIds) {\n const identifiers = getTableIdentifiers(this.occurrence);\n if (!identifiers.id) {\n throw new Error(\n `useEntityIds is true but TableOccurrence \"${identifiers.name}\" does not have an fmtId defined`\n );\n }\n return identifiers.id;\n }\n\n return this.occurrence.getTableName();\n }\n\n async execute<EO extends ExecuteOptions>(\n options?: RequestInit & FFetchOptions & EO,\n ): Promise<\n Result<\n ReturnPreference extends \"minimal\"\n ? { ROWID: number }\n : ConditionallyWithODataAnnotations<\n T,\n EO[\"includeODataAnnotations\"] extends true ? true : false\n >\n >\n > {\n // Merge database-level useEntityIds with per-request options\n const mergedOptions = this.mergeExecuteOptions(options);\n \n // Get table identifier with override support\n const tableId = this.getTableId(mergedOptions.useEntityIds);\n const url = `/${this.databaseName}/${tableId}`;\n\n // Transform field names to FMFIDs if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = mergedOptions.useEntityIds ?? false;\n \n const transformedData = this.occurrence?.baseTable && shouldUseIds\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\n\n // Set Prefer header based on return preference\n const preferHeader =\n this.returnPreference === \"minimal\"\n ? \"return=minimal\"\n : \"return=representation\";\n\n // Make POST request with JSON body\n const result = await this.context._makeRequest<any>(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Prefer: preferHeader,\n ...((mergedOptions as any)?.headers || {}),\n },\n body: JSON.stringify(transformedData),\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n // Handle return=minimal case\n if (this.returnPreference === \"minimal\") {\n // The response should be empty (204 No Content)\n // _makeRequest will return { _location: string } when there's a Location header\n const responseData = result.data as any;\n\n if (!responseData || !responseData._location) {\n throw new InvalidLocationHeaderError(\n \"Location header is required when using return=minimal but was not found in response\",\n );\n }\n\n const rowid = this.parseLocationHeader(responseData._location);\n return { data: { ROWID: rowid } as any, error: undefined };\n }\n\n let response = result.data;\n\n // Transform response field IDs back to names if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n if (this.occurrence?.baseTable && shouldUseIds) {\n response = transformResponseFields(\n response,\n this.occurrence.baseTable,\n undefined, // No expand configs for insert\n );\n }\n\n // Get schema from occurrence if available\n const schema = this.occurrence?.baseTable?.schema;\n\n // Validate the response (FileMaker returns the created record)\n const validation = await validateSingleResponse<T>(\n response,\n schema,\n undefined, // No selected fields for insert\n undefined, // No expand configs\n \"exact\", // Expect exactly one record\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Handle null response (shouldn't happen for insert, but handle it)\n if (validation.data === null) {\n return {\n data: undefined,\n error: new Error(\"Insert operation returned null response\"),\n };\n }\n\n // Strip OData annotations unless explicitly requested\n const finalData = this.stripODataAnnotationsIfNeeded(\n validation.data,\n options,\n );\n\n return { data: finalData as any, error: undefined };\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n // For batch operations, use database-level setting (no per-request override available here)\n const tableId = this.getTableId(this.databaseUseEntityIds);\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData = this.occurrence?.baseTable && this.databaseUseEntityIds\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\n\n return {\n method: \"POST\",\n url: `/${this.databaseName}/${tableId}`,\n body: JSON.stringify(transformedData),\n };\n }\n\n toRequest(baseUrl: string): Request {\n const config = this.getRequestConfig();\n const fullUrl = `${baseUrl}${config.url}`;\n\n // Set Prefer header based on return preference\n const preferHeader =\n this.returnPreference === \"minimal\"\n ? \"return=minimal\"\n : \"return=representation\";\n\n return new Request(fullUrl, {\n method: config.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n Prefer: preferHeader,\n },\n body: config.body,\n });\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { ROWID: number } : T>\n > {\n // Handle 204 No Content (common in batch/changeset operations)\n // FileMaker uses return=minimal for changeset operations regardless of Prefer header\n if (response.status === 204) {\n // Check for Location header (for return=minimal)\n if (this.returnPreference === \"minimal\") {\n const locationHeader =\n response.headers.get(\"Location\") || response.headers.get(\"location\");\n if (locationHeader) {\n const rowid = this.parseLocationHeader(locationHeader);\n return { data: { ROWID: rowid } as any, error: undefined };\n }\n throw new InvalidLocationHeaderError(\n \"Location header is required when using return=minimal but was not found in response\",\n );\n }\n\n // For 204 responses without return=minimal, FileMaker doesn't return the created entity\n // This is valid OData behavior for changeset operations\n // We return a success indicator but no actual data\n return {\n data: {} as any,\n error: undefined,\n };\n }\n\n // If we expected return=minimal but got a body, that's unexpected\n if (this.returnPreference === \"minimal\") {\n throw new InvalidLocationHeaderError(\n \"Expected 204 No Content for return=minimal, but received response with body\",\n );\n }\n\n let rawResponse;\n try {\n rawResponse = await response.json();\n } catch (err) {\n // If parsing fails with 204, handle it gracefully\n if (response.status === 204) {\n return {\n data: {} as any,\n error: undefined,\n };\n }\n return {\n data: undefined,\n error: {\n name: \"ResponseParseError\",\n message: `Failed to parse response JSON: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n timestamp: new Date(),\n } as any,\n };\n }\n\n // Transform response field IDs back to names if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = options?.useEntityIds ?? this.databaseUseEntityIds;\n \n let transformedResponse = rawResponse;\n if (this.occurrence?.baseTable && shouldUseIds) {\n transformedResponse = transformResponseFields(\n rawResponse,\n this.occurrence.baseTable,\n undefined, // No expand configs for insert\n );\n }\n\n // Get schema from occurrence if available\n const schema = this.occurrence?.baseTable?.schema;\n\n // Validate the response (FileMaker returns the created record)\n const validation = await validateSingleResponse<T>(\n transformedResponse,\n schema,\n undefined, // No selected fields for insert\n undefined, // No expand configs\n \"exact\", // Expect exactly one record\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Handle null response (shouldn't happen for insert, but handle it)\n if (validation.data === null) {\n return {\n data: undefined,\n error: new Error(\"Insert operation returned null response\"),\n };\n }\n\n // Strip OData annotations unless explicitly requested\n const finalData = this.stripODataAnnotationsIfNeeded(\n validation.data,\n options,\n );\n\n return { data: finalData as any, error: undefined };\n }\n}\n"],"names":[],"mappings":"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"}
|