@proofkit/fmodata 0.1.0-alpha.6 → 0.1.0-alpha.7
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 +333 -3
- 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 +43 -11
- package/dist/esm/client/database.js +64 -10
- 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 +76 -9
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +15 -4
- package/dist/esm/client/entity-set.js +23 -7
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/filemaker-odata.d.ts +11 -5
- package/dist/esm/client/filemaker-odata.js +46 -14
- 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 +195 -9
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query-builder.d.ts +19 -3
- package/dist/esm/client/query-builder.js +193 -17
- package/dist/esm/client/query-builder.js.map +1 -1
- package/dist/esm/client/record-builder.d.ts +17 -2
- package/dist/esm/client/record-builder.js +87 -5
- 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/update-builder.d.ts +34 -11
- package/dist/esm/client/update-builder.js +119 -19
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/errors.d.ts +14 -1
- package/dist/esm/errors.js +26 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -1
- package/dist/esm/transform.d.ts +9 -0
- package/dist/esm/transform.js +7 -0
- package/dist/esm/transform.js.map +1 -1
- package/dist/esm/types.d.ts +69 -1
- package/package.json +1 -1
- package/src/client/batch-builder.ts +265 -0
- package/src/client/batch-request.ts +485 -0
- package/src/client/database.ts +106 -52
- package/src/client/delete-builder.ts +116 -14
- package/src/client/entity-set.ts +80 -6
- package/src/client/filemaker-odata.ts +65 -19
- package/src/client/insert-builder.ts +296 -18
- package/src/client/query-builder.ts +278 -17
- package/src/client/record-builder.ts +119 -11
- package/src/client/response-processor.ts +103 -0
- package/src/client/schema-manager.ts +246 -0
- package/src/client/update-builder.ts +195 -37
- package/src/errors.ts +33 -1
- package/src/index.ts +13 -0
- package/src/transform.ts +19 -6
- package/src/types.ts +89 -1
|
@@ -2,7 +2,7 @@ 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 { QueryBuilder } from "./query-builder.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getTableIdentifiers, transformFieldNamesToIds } from "../transform.js";
|
|
6
6
|
class UpdateBuilder {
|
|
7
7
|
constructor(config) {
|
|
8
8
|
__publicField(this, "tableName");
|
|
@@ -10,15 +10,19 @@ class UpdateBuilder {
|
|
|
10
10
|
__publicField(this, "context");
|
|
11
11
|
__publicField(this, "occurrence");
|
|
12
12
|
__publicField(this, "data");
|
|
13
|
+
__publicField(this, "returnPreference");
|
|
14
|
+
__publicField(this, "databaseUseEntityIds");
|
|
13
15
|
this.occurrence = config.occurrence;
|
|
14
16
|
this.tableName = config.tableName;
|
|
15
17
|
this.databaseName = config.databaseName;
|
|
16
18
|
this.context = config.context;
|
|
17
19
|
this.data = config.data;
|
|
20
|
+
this.returnPreference = config.returnPreference;
|
|
21
|
+
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
18
22
|
}
|
|
19
23
|
/**
|
|
20
24
|
* Update a single record by ID
|
|
21
|
-
* Returns
|
|
25
|
+
* Returns updated count by default, or full record if returnFullRecord was set to true
|
|
22
26
|
*/
|
|
23
27
|
byId(id) {
|
|
24
28
|
return new ExecutableUpdateBuilder({
|
|
@@ -28,12 +32,14 @@ class UpdateBuilder {
|
|
|
28
32
|
context: this.context,
|
|
29
33
|
data: this.data,
|
|
30
34
|
mode: "byId",
|
|
31
|
-
recordId: id
|
|
35
|
+
recordId: id,
|
|
36
|
+
returnPreference: this.returnPreference,
|
|
37
|
+
databaseUseEntityIds: this.databaseUseEntityIds
|
|
32
38
|
});
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
35
41
|
* Update records matching a filter query
|
|
36
|
-
* Returns
|
|
42
|
+
* Returns updated count by default, or full record if returnFullRecord was set to true
|
|
37
43
|
* @param fn Callback that receives a QueryBuilder for building the filter
|
|
38
44
|
*/
|
|
39
45
|
where(fn) {
|
|
@@ -51,7 +57,9 @@ class UpdateBuilder {
|
|
|
51
57
|
context: this.context,
|
|
52
58
|
data: this.data,
|
|
53
59
|
mode: "byFilter",
|
|
54
|
-
queryBuilder: configuredBuilder
|
|
60
|
+
queryBuilder: configuredBuilder,
|
|
61
|
+
returnPreference: this.returnPreference,
|
|
62
|
+
databaseUseEntityIds: this.databaseUseEntityIds
|
|
55
63
|
});
|
|
56
64
|
}
|
|
57
65
|
}
|
|
@@ -65,6 +73,8 @@ class ExecutableUpdateBuilder {
|
|
|
65
73
|
__publicField(this, "mode");
|
|
66
74
|
__publicField(this, "recordId");
|
|
67
75
|
__publicField(this, "queryBuilder");
|
|
76
|
+
__publicField(this, "returnPreference");
|
|
77
|
+
__publicField(this, "databaseUseEntityIds");
|
|
68
78
|
this.occurrence = config.occurrence;
|
|
69
79
|
this.tableName = config.tableName;
|
|
70
80
|
this.databaseName = config.databaseName;
|
|
@@ -73,11 +83,46 @@ class ExecutableUpdateBuilder {
|
|
|
73
83
|
this.mode = config.mode;
|
|
74
84
|
this.recordId = config.recordId;
|
|
75
85
|
this.queryBuilder = config.queryBuilder;
|
|
86
|
+
this.returnPreference = config.returnPreference;
|
|
87
|
+
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Helper to merge database-level useEntityIds with per-request options
|
|
91
|
+
*/
|
|
92
|
+
mergeExecuteOptions(options) {
|
|
93
|
+
return {
|
|
94
|
+
...options,
|
|
95
|
+
useEntityIds: (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
|
|
100
|
+
* @param useEntityIds - Optional override for entity ID usage
|
|
101
|
+
*/
|
|
102
|
+
getTableId(useEntityIds) {
|
|
103
|
+
var _a, _b;
|
|
104
|
+
if (!this.occurrence) {
|
|
105
|
+
return this.tableName;
|
|
106
|
+
}
|
|
107
|
+
const contextDefault = ((_b = (_a = this.context)._getUseEntityIds) == null ? void 0 : _b.call(_a)) ?? false;
|
|
108
|
+
const shouldUseIds = useEntityIds ?? contextDefault;
|
|
109
|
+
if (shouldUseIds) {
|
|
110
|
+
const identifiers = getTableIdentifiers(this.occurrence);
|
|
111
|
+
if (!identifiers.id) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`useEntityIds is true but TableOccurrence "${identifiers.name}" does not have an fmtId defined`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return identifiers.id;
|
|
117
|
+
}
|
|
118
|
+
return this.occurrence.getTableName();
|
|
76
119
|
}
|
|
77
120
|
async execute(options) {
|
|
78
121
|
var _a;
|
|
79
|
-
const
|
|
80
|
-
const
|
|
122
|
+
const mergedOptions = this.mergeExecuteOptions(options);
|
|
123
|
+
const tableId = this.getTableId(mergedOptions.useEntityIds);
|
|
124
|
+
const shouldUseIds = mergedOptions.useEntityIds ?? false;
|
|
125
|
+
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) && shouldUseIds ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
81
126
|
let url;
|
|
82
127
|
if (this.mode === "byId") {
|
|
83
128
|
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
@@ -89,30 +134,44 @@ class ExecutableUpdateBuilder {
|
|
|
89
134
|
const queryParams = queryString.startsWith(`/${tableId}`) ? queryString.slice(`/${tableId}`.length) : queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
|
|
90
135
|
url = `/${this.databaseName}/${tableId}${queryParams}`;
|
|
91
136
|
}
|
|
137
|
+
const headers = {
|
|
138
|
+
"Content-Type": "application/json"
|
|
139
|
+
};
|
|
140
|
+
if (this.returnPreference === "representation") {
|
|
141
|
+
headers["Prefer"] = "return=representation";
|
|
142
|
+
}
|
|
92
143
|
const result = await this.context._makeRequest(url, {
|
|
93
144
|
method: "PATCH",
|
|
94
|
-
headers
|
|
95
|
-
"Content-Type": "application/json"
|
|
96
|
-
},
|
|
145
|
+
headers,
|
|
97
146
|
body: JSON.stringify(transformedData),
|
|
98
|
-
...
|
|
147
|
+
...mergedOptions
|
|
99
148
|
});
|
|
100
149
|
if (result.error) {
|
|
101
150
|
return { data: void 0, error: result.error };
|
|
102
151
|
}
|
|
103
152
|
const response = result.data;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
153
|
+
if (this.returnPreference === "representation") {
|
|
154
|
+
return {
|
|
155
|
+
data: response,
|
|
156
|
+
error: void 0
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
let updatedCount = 0;
|
|
160
|
+
if (typeof response === "number") {
|
|
161
|
+
updatedCount = response;
|
|
162
|
+
} else if (response && typeof response === "object") {
|
|
163
|
+
updatedCount = response.updatedCount || 0;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
data: { updatedCount },
|
|
167
|
+
error: void 0
|
|
168
|
+
};
|
|
109
169
|
}
|
|
110
|
-
return { data: { updatedCount }, error: void 0 };
|
|
111
170
|
}
|
|
112
171
|
getRequestConfig() {
|
|
113
172
|
var _a;
|
|
114
|
-
const tableId = this.
|
|
115
|
-
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
173
|
+
const tableId = this.getTableId(this.databaseUseEntityIds);
|
|
174
|
+
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) && this.databaseUseEntityIds ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
116
175
|
let url;
|
|
117
176
|
if (this.mode === "byId") {
|
|
118
177
|
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
@@ -130,6 +189,47 @@ class ExecutableUpdateBuilder {
|
|
|
130
189
|
body: JSON.stringify(transformedData)
|
|
131
190
|
};
|
|
132
191
|
}
|
|
192
|
+
toRequest(baseUrl) {
|
|
193
|
+
const config = this.getRequestConfig();
|
|
194
|
+
const fullUrl = `${baseUrl}${config.url}`;
|
|
195
|
+
return new Request(fullUrl, {
|
|
196
|
+
method: config.method,
|
|
197
|
+
headers: {
|
|
198
|
+
"Content-Type": "application/json",
|
|
199
|
+
Accept: "application/json"
|
|
200
|
+
},
|
|
201
|
+
body: config.body
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
async processResponse(response, options) {
|
|
205
|
+
const text = await response.text();
|
|
206
|
+
if (!text || text.trim() === "") {
|
|
207
|
+
const affectedRows = response.headers.get("fmodata.affected_rows");
|
|
208
|
+
const updatedCount = affectedRows ? parseInt(affectedRows, 10) : 1;
|
|
209
|
+
return {
|
|
210
|
+
data: { updatedCount },
|
|
211
|
+
error: void 0
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const rawResponse = JSON.parse(text);
|
|
215
|
+
if (this.returnPreference === "representation") {
|
|
216
|
+
return {
|
|
217
|
+
data: rawResponse,
|
|
218
|
+
error: void 0
|
|
219
|
+
};
|
|
220
|
+
} else {
|
|
221
|
+
let updatedCount = 0;
|
|
222
|
+
if (typeof rawResponse === "number") {
|
|
223
|
+
updatedCount = rawResponse;
|
|
224
|
+
} else if (rawResponse && typeof rawResponse === "object") {
|
|
225
|
+
updatedCount = rawResponse.updatedCount || 0;
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
data: { updatedCount },
|
|
229
|
+
error: void 0
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
133
233
|
}
|
|
134
234
|
export {
|
|
135
235
|
ExecutableUpdateBuilder,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport type { BaseTable } from \"./base-table\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport {\n transformFieldNamesToIds,\n transformTableName,\n} from \"../transform\";\n\n/**\n * Initial update builder returned from EntitySet.update(data)\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class UpdateBuilder<\n T extends Record<string, any>,\n BT extends BaseTable<any, any, any, any>,\n> {\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\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 /**\n * Update a single record by ID\n * Returns the count of updated records (0 or 1)\n */\n byId(id: string | number): ExecutableUpdateBuilder<T, true> {\n return new ExecutableUpdateBuilder<T, true>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns the count of updated records\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(\n fn: (\n q: QueryBuilder<WithSystemFields<T>>,\n ) => QueryBuilder<WithSystemFields<T>>,\n ): ExecutableUpdateBuilder<T, true> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<\n WithSystemFields<T>,\n keyof WithSystemFields<T>,\n false,\n false,\n undefined\n >({\n occurrence: undefined,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Let the user configure it\n const configuredBuilder = fn(queryBuilder);\n\n return new ExecutableUpdateBuilder<T, true>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Both modes return the count of updated records\n */\nexport class ExecutableUpdateBuilder<\n T extends Record<string, any>,\n IsByFilter extends boolean,\n> implements ExecutableBuilder<{ updatedCount: number }>\n{\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<any>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<any>;\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.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n }\n\n async execute(\n options?: RequestInit & FFetchOptions,\n ): Promise<Result<{ updatedCount: number }>> {\n // Transform table name to FMTID if using entity IDs\n const tableId = this.occurrence\n ? transformTableName(this.occurrence)\n : this.tableName;\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData = this.occurrence?.baseTable\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Update by filter: PATCH /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\n // The query string will have the tableId already transformed by QueryBuilder\n // Remove the leading \"/\" and table name from the query string as we'll build our own URL\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(transformedData),\n ...options,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Both byId and byFilter return affected row count\n let updatedCount = 0;\n\n if (typeof response === \"number\") {\n updatedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (response as any).updatedCount || 0;\n }\n\n return { data: { updatedCount }, error: undefined };\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n // Transform table name to FMTID if using entity IDs\n const tableId = this.occurrence\n ? transformTableName(this.occurrence)\n : this.tableName;\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData = this.occurrence?.baseTable\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n const queryString = this.queryBuilder.getQueryString();\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\n body: JSON.stringify(transformedData),\n };\n }\n}\n"],"names":[],"mappings":";;;;;AAmBO,MAAM,cAGX;AAAA,EAOA,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;AAAA;AAAA;AAAA;AAAA,EAOrB,KAAK,IAAuD;AAC1D,WAAO,IAAI,wBAAiC;AAAA,MAC1C,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQH,MACE,IAGkC;AAE5B,UAAA,eAAe,IAAI,aAMvB;AAAA,MACA,YAAY;AAAA,MACZ,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,IAAA,CACf;AAGK,UAAA,oBAAoB,GAAG,YAAY;AAEzC,WAAO,IAAI,wBAAiC;AAAA,MAC1C,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,MAAM;AAAA,MACN,cAAc;AAAA,IAAA,CACf;AAAA,EAAA;AAEL;AAOO,MAAM,wBAIb;AAAA,EAUE,YAAY,QAST;AAlBK;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAYN,SAAK,aAAa,OAAO;AACzB,SAAK,YAAY,OAAO;AACxB,SAAK,eAAe,OAAO;AAC3B,SAAK,UAAU,OAAO;AACtB,SAAK,OAAO,OAAO;AACnB,SAAK,OAAO,OAAO;AACnB,SAAK,WAAW,OAAO;AACvB,SAAK,eAAe,OAAO;AAAA,EAAA;AAAA,EAG7B,MAAM,QACJ,SAC2C;;AAE3C,UAAM,UAAU,KAAK,aACjB,mBAAmB,KAAK,UAAU,IAClC,KAAK;AAGH,UAAA,oBAAkB,UAAK,eAAL,mBAAiB,aACrC,yBAAyB,KAAK,MAAM,KAAK,WAAW,SAAS,IAC7D,KAAK;AAEL,QAAA;AAEA,QAAA,KAAK,SAAS,QAAQ;AAExB,YAAM,IAAI,KAAK,YAAY,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAA,IAAA,OACnD;AAED,UAAA,CAAC,KAAK,cAAc;AAChB,cAAA,IAAI,MAAM,mDAAmD;AAAA,MAAA;AAI/D,YAAA,cAAc,KAAK,aAAa,eAAe;AAGrD,YAAM,cAAc,YAAY,WAAW,IAAI,OAAO,EAAE,IACpD,YAAY,MAAM,IAAI,OAAO,GAAG,MAAM,IACtC,YAAY,WAAW,IAAI,KAAK,SAAS,EAAE,IACzC,YAAY,MAAM,IAAI,KAAK,SAAS,GAAG,MAAM,IAC7C;AAEN,YAAM,IAAI,KAAK,YAAY,IAAI,OAAO,GAAG,WAAW;AAAA,IAAA;AAItD,UAAM,SAAS,MAAM,KAAK,QAAQ,aAAa,KAAK;AAAA,MAClD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,eAAe;AAAA,MACpC,GAAG;AAAA,IAAA,CACJ;AAED,QAAI,OAAO,OAAO;AAChB,aAAO,EAAE,MAAM,QAAW,OAAO,OAAO,MAAM;AAAA,IAAA;AAGhD,UAAM,WAAW,OAAO;AAGxB,QAAI,eAAe;AAEf,QAAA,OAAO,aAAa,UAAU;AACjB,qBAAA;AAAA,IACN,WAAA,YAAY,OAAO,aAAa,UAAU;AAEnD,qBAAgB,SAAiB,gBAAgB;AAAA,IAAA;AAGnD,WAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,OAAU;AAAA,EAAA;AAAA,EAGpD,mBAAgE;;AAE9D,UAAM,UAAU,KAAK,aACjB,mBAAmB,KAAK,UAAU,IAClC,KAAK;AAGH,UAAA,oBAAkB,UAAK,eAAL,mBAAiB,aACrC,yBAAyB,KAAK,MAAM,KAAK,WAAW,SAAS,IAC7D,KAAK;AAEL,QAAA;AAEA,QAAA,KAAK,SAAS,QAAQ;AACxB,YAAM,IAAI,KAAK,YAAY,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAA,IAAA,OACnD;AACD,UAAA,CAAC,KAAK,cAAc;AAChB,cAAA,IAAI,MAAM,mDAAmD;AAAA,MAAA;AAG/D,YAAA,cAAc,KAAK,aAAa,eAAe;AACrD,YAAM,cAAc,YAAY,WAAW,IAAI,OAAO,EAAE,IACpD,YAAY,MAAM,IAAI,OAAO,GAAG,MAAM,IACtC,YAAY,WAAW,IAAI,KAAK,SAAS,EAAE,IACzC,YAAY,MAAM,IAAI,KAAK,SAAS,GAAG,MAAM,IAC7C;AAEN,YAAM,IAAI,KAAK,YAAY,IAAI,OAAO,GAAG,WAAW;AAAA,IAAA;AAG/C,WAAA;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,eAAe;AAAA,IACtC;AAAA,EAAA;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n ExecuteOptions,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport type { BaseTable } from \"./base-table\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport {\n transformFieldNamesToIds,\n transformTableName,\n getTableIdentifiers,\n} from \"../transform\";\n\n/**\n * Initial update builder returned from EntitySet.update(data)\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class UpdateBuilder<\n T extends Record<string, any>,\n BT extends BaseTable<any, any, any, any>,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> {\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n private returnPreference: ReturnPreference;\n\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\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 this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Update a single record by ID\n * Returns updated count by default, or full record if returnFullRecord was set to true\n */\n byId(\n id: string | number,\n ): ExecutableUpdateBuilder<T, true, ReturnPreference> {\n return new ExecutableUpdateBuilder<T, true, ReturnPreference>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns updated count by default, or full record if returnFullRecord was set to true\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(\n fn: (\n q: QueryBuilder<WithSystemFields<T>>,\n ) => QueryBuilder<WithSystemFields<T>>,\n ): ExecutableUpdateBuilder<T, true, ReturnPreference> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<\n WithSystemFields<T>,\n keyof WithSystemFields<T>,\n false,\n false,\n undefined\n >({\n occurrence: undefined,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Let the user configure it\n const configuredBuilder = fn(queryBuilder);\n\n return new ExecutableUpdateBuilder<T, true, ReturnPreference>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Can return either updated count or full record based on returnFullRecord option\n */\nexport class ExecutableUpdateBuilder<\n T extends Record<string, any>,\n IsByFilter extends boolean,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> implements\n ExecutableBuilder<\n ReturnPreference extends \"minimal\" ? { updatedCount: number } : T\n >\n{\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<any>;\n private returnPreference: ReturnPreference;\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<any>;\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.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n this.returnPreference = config.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 * 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(\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { updatedCount: number } : T>\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\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 =\n this.occurrence?.baseTable && shouldUseIds\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Update by filter: PATCH /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\n // The query string will have the tableId already transformed by QueryBuilder\n // Remove the leading \"/\" and table name from the query string as we'll build our own URL\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Set Prefer header based on returnPreference\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.returnPreference === \"representation\") {\n headers[\"Prefer\"] = \"return=representation\";\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers,\n body: JSON.stringify(transformedData),\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: response as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : T,\n error: undefined,\n };\n } else {\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof response === \"number\") {\n updatedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (response as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : T,\n error: undefined,\n };\n }\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 =\n this.occurrence?.baseTable && this.databaseUseEntityIds\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n const queryString = this.queryBuilder.getQueryString();\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\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 return new Request(fullUrl, {\n method: config.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: config.body,\n });\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { updatedCount: number } : T>\n > {\n // Check for empty response (204 No Content)\n const text = await response.text();\n if (!text || text.trim() === \"\") {\n // For 204 No Content, check the fmodata.affected_rows header\n const affectedRows = response.headers.get(\"fmodata.affected_rows\");\n const updatedCount = affectedRows ? parseInt(affectedRows, 10) : 1;\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : T,\n error: undefined,\n };\n }\n\n const rawResponse = JSON.parse(text);\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: rawResponse as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : T,\n error: undefined,\n };\n } else {\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof rawResponse === \"number\") {\n updatedCount = rawResponse;\n } else if (rawResponse && typeof rawResponse === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (rawResponse as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : T,\n error: undefined,\n };\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/errors.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export declare class ODataError extends FMODataError {
|
|
|
27
27
|
readonly details?: any;
|
|
28
28
|
constructor(url: string, message: string, code?: string, details?: any);
|
|
29
29
|
}
|
|
30
|
+
export declare class SchemaLockedError extends FMODataError {
|
|
31
|
+
readonly kind: "SchemaLockedError";
|
|
32
|
+
readonly url: string;
|
|
33
|
+
readonly code: string;
|
|
34
|
+
readonly details?: any;
|
|
35
|
+
constructor(url: string, message: string, details?: any);
|
|
36
|
+
}
|
|
30
37
|
export declare class ValidationError extends FMODataError {
|
|
31
38
|
readonly kind: "ValidationError";
|
|
32
39
|
readonly field?: string;
|
|
@@ -50,11 +57,17 @@ export declare class RecordCountMismatchError extends FMODataError {
|
|
|
50
57
|
readonly received: number;
|
|
51
58
|
constructor(expected: number | "one" | "at-most-one", received: number);
|
|
52
59
|
}
|
|
60
|
+
export declare class InvalidLocationHeaderError extends FMODataError {
|
|
61
|
+
readonly kind: "InvalidLocationHeaderError";
|
|
62
|
+
readonly locationHeader?: string;
|
|
63
|
+
constructor(message: string, locationHeader?: string);
|
|
64
|
+
}
|
|
53
65
|
export declare function isHTTPError(error: unknown): error is HTTPError;
|
|
54
66
|
export declare function isValidationError(error: unknown): error is ValidationError;
|
|
55
67
|
export declare function isODataError(error: unknown): error is ODataError;
|
|
68
|
+
export declare function isSchemaLockedError(error: unknown): error is SchemaLockedError;
|
|
56
69
|
export declare function isResponseStructureError(error: unknown): error is ResponseStructureError;
|
|
57
70
|
export declare function isRecordCountMismatchError(error: unknown): error is RecordCountMismatchError;
|
|
58
71
|
export declare function isFMODataError(error: unknown): error is FMODataError;
|
|
59
72
|
export type { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError, } from '@fetchkit/ffetch';
|
|
60
|
-
export type FMODataErrorType = import('@fetchkit/ffetch').TimeoutError | import('@fetchkit/ffetch').AbortError | import('@fetchkit/ffetch').NetworkError | import('@fetchkit/ffetch').RetryLimitError | import('@fetchkit/ffetch').CircuitOpenError | HTTPError | ODataError | ValidationError | ResponseStructureError | RecordCountMismatchError;
|
|
73
|
+
export type FMODataErrorType = import('@fetchkit/ffetch').TimeoutError | import('@fetchkit/ffetch').AbortError | import('@fetchkit/ffetch').NetworkError | import('@fetchkit/ffetch').RetryLimitError | import('@fetchkit/ffetch').CircuitOpenError | HTTPError | ODataError | SchemaLockedError | ValidationError | ResponseStructureError | RecordCountMismatchError | InvalidLocationHeaderError;
|
package/dist/esm/errors.js
CHANGED
|
@@ -51,6 +51,18 @@ class ODataError extends FMODataError {
|
|
|
51
51
|
this.details = details;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
class SchemaLockedError extends FMODataError {
|
|
55
|
+
constructor(url, message, details) {
|
|
56
|
+
super(`OData error: ${message}`);
|
|
57
|
+
__publicField(this, "kind", "SchemaLockedError");
|
|
58
|
+
__publicField(this, "url");
|
|
59
|
+
__publicField(this, "code");
|
|
60
|
+
__publicField(this, "details");
|
|
61
|
+
this.url = url;
|
|
62
|
+
this.code = "303";
|
|
63
|
+
this.details = details;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
54
66
|
class ValidationError extends FMODataError {
|
|
55
67
|
constructor(message, issues, options) {
|
|
56
68
|
super(
|
|
@@ -87,6 +99,14 @@ class RecordCountMismatchError extends FMODataError {
|
|
|
87
99
|
this.received = received;
|
|
88
100
|
}
|
|
89
101
|
}
|
|
102
|
+
class InvalidLocationHeaderError extends FMODataError {
|
|
103
|
+
constructor(message, locationHeader) {
|
|
104
|
+
super(message);
|
|
105
|
+
__publicField(this, "kind", "InvalidLocationHeaderError");
|
|
106
|
+
__publicField(this, "locationHeader");
|
|
107
|
+
this.locationHeader = locationHeader;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
90
110
|
function isHTTPError(error) {
|
|
91
111
|
return error instanceof HTTPError;
|
|
92
112
|
}
|
|
@@ -96,6 +116,9 @@ function isValidationError(error) {
|
|
|
96
116
|
function isODataError(error) {
|
|
97
117
|
return error instanceof ODataError;
|
|
98
118
|
}
|
|
119
|
+
function isSchemaLockedError(error) {
|
|
120
|
+
return error instanceof SchemaLockedError;
|
|
121
|
+
}
|
|
99
122
|
function isResponseStructureError(error) {
|
|
100
123
|
return error instanceof ResponseStructureError;
|
|
101
124
|
}
|
|
@@ -108,15 +131,18 @@ function isFMODataError(error) {
|
|
|
108
131
|
export {
|
|
109
132
|
FMODataError,
|
|
110
133
|
HTTPError,
|
|
134
|
+
InvalidLocationHeaderError,
|
|
111
135
|
ODataError,
|
|
112
136
|
RecordCountMismatchError,
|
|
113
137
|
ResponseStructureError,
|
|
138
|
+
SchemaLockedError,
|
|
114
139
|
ValidationError,
|
|
115
140
|
isFMODataError,
|
|
116
141
|
isHTTPError,
|
|
117
142
|
isODataError,
|
|
118
143
|
isRecordCountMismatchError,
|
|
119
144
|
isResponseStructureError,
|
|
145
|
+
isSchemaLockedError,
|
|
120
146
|
isValidationError
|
|
121
147
|
};
|
|
122
148
|
//# sourceMappingURL=errors.js.map
|
package/dist/esm/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Base class for all fmodata errors\n */\nexport abstract class FMODataError extends Error {\n abstract readonly kind: string;\n readonly timestamp: Date;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = this.constructor.name;\n this.timestamp = new Date();\n }\n}\n\n// ============================================\n// HTTP Errors (with status codes)\n// ============================================\n\nexport class HTTPError extends FMODataError {\n readonly kind = \"HTTPError\" as const;\n readonly url: string;\n readonly status: number;\n readonly statusText: string;\n readonly response?: any;\n\n constructor(url: string, status: number, statusText: string, response?: any) {\n super(`HTTP ${status} ${statusText} for ${url}`);\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n this.response = response;\n }\n\n // Helper methods for common status checks\n is4xx(): boolean {\n return this.status >= 400 && this.status < 500;\n }\n\n is5xx(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isForbidden(): boolean {\n return this.status === 403;\n }\n}\n\n// ============================================\n// OData Specific Errors\n// ============================================\n\nexport class ODataError extends FMODataError {\n readonly kind = \"ODataError\" as const;\n readonly url: string;\n readonly code?: string;\n readonly details?: any;\n\n constructor(url: string, message: string, code?: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = code;\n this.details = details;\n }\n}\n\n// ============================================\n// Validation Errors\n// ============================================\n\nexport class ValidationError extends FMODataError {\n readonly kind = \"ValidationError\" as const;\n readonly field?: string;\n readonly issues: readonly StandardSchemaV1.Issue[];\n readonly value?: unknown;\n\n constructor(\n message: string,\n issues: readonly StandardSchemaV1.Issue[],\n options?: {\n field?: string;\n value?: unknown;\n cause?: Error[\"cause\"];\n },\n ) {\n super(\n message,\n options?.cause !== undefined ? { cause: options.cause } : undefined,\n );\n this.field = options?.field;\n this.issues = issues;\n this.value = options?.value;\n }\n}\n\nexport class ResponseStructureError extends FMODataError {\n readonly kind = \"ResponseStructureError\" as const;\n readonly expected: string;\n readonly received: any;\n\n constructor(expected: string, received: any) {\n super(`Invalid response structure: expected ${expected}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class RecordCountMismatchError extends FMODataError {\n readonly kind = \"RecordCountMismatchError\" as const;\n readonly expected: number | \"one\" | \"at-most-one\";\n readonly received: number;\n\n constructor(expected: number | \"one\" | \"at-most-one\", received: number) {\n const expectedStr = typeof expected === \"number\" ? expected : expected;\n super(`Expected ${expectedStr} record(s), but received ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\n// ============================================\n// Type Guards\n// ============================================\n\nexport function isHTTPError(error: unknown): error is HTTPError {\n return error instanceof HTTPError;\n}\n\nexport function isValidationError(error: unknown): error is ValidationError {\n return error instanceof ValidationError;\n}\n\nexport function isODataError(error: unknown): error is ODataError {\n return error instanceof ODataError;\n}\n\nexport function isResponseStructureError(\n error: unknown,\n): error is ResponseStructureError {\n return error instanceof ResponseStructureError;\n}\n\nexport function isRecordCountMismatchError(\n error: unknown,\n): error is RecordCountMismatchError {\n return error instanceof RecordCountMismatchError;\n}\n\nexport function isFMODataError(error: unknown): error is FMODataError {\n return error instanceof FMODataError;\n}\n\n// ============================================\n// Union type for all possible errors\n// ============================================\n\n// Re-export ffetch errors (they'll be imported from @fetchkit/ffetch)\nexport type {\n TimeoutError,\n AbortError,\n NetworkError,\n RetryLimitError,\n CircuitOpenError,\n} from \"@fetchkit/ffetch\";\n\nexport type FMODataErrorType =\n | import(\"@fetchkit/ffetch\").TimeoutError\n | import(\"@fetchkit/ffetch\").AbortError\n | import(\"@fetchkit/ffetch\").NetworkError\n | import(\"@fetchkit/ffetch\").RetryLimitError\n | import(\"@fetchkit/ffetch\").CircuitOpenError\n | HTTPError\n | ODataError\n | ValidationError\n | ResponseStructureError\n | RecordCountMismatchError;\n"],"names":[],"mappings":";;;AAKO,MAAe,qBAAqB,MAAM;AAAA,EAI/C,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AAHf;AAIF,SAAA,OAAO,KAAK,YAAY;AACxB,SAAA,gCAAgB,KAAK;AAAA,EAAA;AAE9B;AAMO,MAAM,kBAAkB,aAAa;AAAA,EAO1C,YAAY,KAAa,QAAgB,YAAoB,UAAgB;AAC3E,UAAM,QAAQ,MAAM,IAAI,UAAU,QAAQ,GAAG,EAAE;AAPxC,gCAAO;AACP;AACA;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAAA;AAAA;AAAA,EAIlB,QAAiB;AACf,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS;AAAA,EAAA;AAAA,EAG7C,QAAiB;AACf,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS;AAAA,EAAA;AAAA,EAG7C,aAAsB;AACpB,WAAO,KAAK,WAAW;AAAA,EAAA;AAAA,EAGzB,iBAA0B;AACxB,WAAO,KAAK,WAAW;AAAA,EAAA;AAAA,EAGzB,cAAuB;AACrB,WAAO,KAAK,WAAW;AAAA,EAAA;AAE3B;AAMO,MAAM,mBAAmB,aAAa;AAAA,EAM3C,YAAY,KAAa,SAAiB,MAAe,SAAe;AAChE,UAAA,gBAAgB,OAAO,EAAE;AANxB,gCAAO;AACP;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EAAA;AAEnB;AAMO,MAAM,wBAAwB,aAAa;AAAA,EAMhD,YACE,SACA,QACA,SAKA;AACA;AAAA,MACE;AAAA,OACA,mCAAS,WAAU,SAAY,EAAE,OAAO,QAAQ,UAAU;AAAA,IAC5D;AAjBO,gCAAO;AACP;AACA;AACA;AAeP,SAAK,QAAQ,mCAAS;AACtB,SAAK,SAAS;AACd,SAAK,QAAQ,mCAAS;AAAA,EAAA;AAE1B;AAEO,MAAM,+BAA+B,aAAa;AAAA,EAKvD,YAAY,UAAkB,UAAe;AACrC,UAAA,wCAAwC,QAAQ,EAAE;AALjD,gCAAO;AACP;AACA;AAIP,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAAA;AAEpB;AAEO,MAAM,iCAAiC,aAAa;AAAA,EAKzD,YAAY,UAA0C,UAAkB;AACtE,UAAM,cAAc,OAAO,aAAa,WAAW,WAAW;AAC9D,UAAM,YAAY,WAAW,4BAA4B,QAAQ,EAAE;AAN5D,gCAAO;AACP;AACA;AAKP,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAAA;AAEpB;AAMO,SAAS,YAAY,OAAoC;AAC9D,SAAO,iBAAiB;AAC1B;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,iBAAiB;AAC1B;AAEO,SAAS,aAAa,OAAqC;AAChE,SAAO,iBAAiB;AAC1B;AAEO,SAAS,yBACd,OACiC;AACjC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,2BACd,OACmC;AACnC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,eAAe,OAAuC;AACpE,SAAO,iBAAiB;AAC1B;"}
|
|
1
|
+
{"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Base class for all fmodata errors\n */\nexport abstract class FMODataError extends Error {\n abstract readonly kind: string;\n readonly timestamp: Date;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = this.constructor.name;\n this.timestamp = new Date();\n }\n}\n\n// ============================================\n// HTTP Errors (with status codes)\n// ============================================\n\nexport class HTTPError extends FMODataError {\n readonly kind = \"HTTPError\" as const;\n readonly url: string;\n readonly status: number;\n readonly statusText: string;\n readonly response?: any;\n\n constructor(url: string, status: number, statusText: string, response?: any) {\n super(`HTTP ${status} ${statusText} for ${url}`);\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n this.response = response;\n }\n\n // Helper methods for common status checks\n is4xx(): boolean {\n return this.status >= 400 && this.status < 500;\n }\n\n is5xx(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isForbidden(): boolean {\n return this.status === 403;\n }\n}\n\n// ============================================\n// OData Specific Errors\n// ============================================\n\nexport class ODataError extends FMODataError {\n readonly kind = \"ODataError\" as const;\n readonly url: string;\n readonly code?: string;\n readonly details?: any;\n\n constructor(url: string, message: string, code?: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = code;\n this.details = details;\n }\n}\n\nexport class SchemaLockedError extends FMODataError {\n readonly kind = \"SchemaLockedError\" as const;\n readonly url: string;\n readonly code: string;\n readonly details?: any;\n\n constructor(url: string, message: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = \"303\";\n this.details = details;\n }\n}\n\n// ============================================\n// Validation Errors\n// ============================================\n\nexport class ValidationError extends FMODataError {\n readonly kind = \"ValidationError\" as const;\n readonly field?: string;\n readonly issues: readonly StandardSchemaV1.Issue[];\n readonly value?: unknown;\n\n constructor(\n message: string,\n issues: readonly StandardSchemaV1.Issue[],\n options?: {\n field?: string;\n value?: unknown;\n cause?: Error[\"cause\"];\n },\n ) {\n super(\n message,\n options?.cause !== undefined ? { cause: options.cause } : undefined,\n );\n this.field = options?.field;\n this.issues = issues;\n this.value = options?.value;\n }\n}\n\nexport class ResponseStructureError extends FMODataError {\n readonly kind = \"ResponseStructureError\" as const;\n readonly expected: string;\n readonly received: any;\n\n constructor(expected: string, received: any) {\n super(`Invalid response structure: expected ${expected}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class RecordCountMismatchError extends FMODataError {\n readonly kind = \"RecordCountMismatchError\" as const;\n readonly expected: number | \"one\" | \"at-most-one\";\n readonly received: number;\n\n constructor(expected: number | \"one\" | \"at-most-one\", received: number) {\n const expectedStr = typeof expected === \"number\" ? expected : expected;\n super(`Expected ${expectedStr} record(s), but received ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidLocationHeaderError extends FMODataError {\n readonly kind = \"InvalidLocationHeaderError\" as const;\n readonly locationHeader?: string;\n\n constructor(message: string, locationHeader?: string) {\n super(message);\n this.locationHeader = locationHeader;\n }\n}\n\n// ============================================\n// Type Guards\n// ============================================\n\nexport function isHTTPError(error: unknown): error is HTTPError {\n return error instanceof HTTPError;\n}\n\nexport function isValidationError(error: unknown): error is ValidationError {\n return error instanceof ValidationError;\n}\n\nexport function isODataError(error: unknown): error is ODataError {\n return error instanceof ODataError;\n}\n\nexport function isSchemaLockedError(\n error: unknown,\n): error is SchemaLockedError {\n return error instanceof SchemaLockedError;\n}\n\nexport function isResponseStructureError(\n error: unknown,\n): error is ResponseStructureError {\n return error instanceof ResponseStructureError;\n}\n\nexport function isRecordCountMismatchError(\n error: unknown,\n): error is RecordCountMismatchError {\n return error instanceof RecordCountMismatchError;\n}\n\nexport function isFMODataError(error: unknown): error is FMODataError {\n return error instanceof FMODataError;\n}\n\n// ============================================\n// Union type for all possible errors\n// ============================================\n\n// Re-export ffetch errors (they'll be imported from @fetchkit/ffetch)\nexport type {\n TimeoutError,\n AbortError,\n NetworkError,\n RetryLimitError,\n CircuitOpenError,\n} from \"@fetchkit/ffetch\";\n\nexport type FMODataErrorType =\n | import(\"@fetchkit/ffetch\").TimeoutError\n | import(\"@fetchkit/ffetch\").AbortError\n | import(\"@fetchkit/ffetch\").NetworkError\n | import(\"@fetchkit/ffetch\").RetryLimitError\n | import(\"@fetchkit/ffetch\").CircuitOpenError\n | HTTPError\n | ODataError\n | SchemaLockedError\n | ValidationError\n | ResponseStructureError\n | RecordCountMismatchError\n | InvalidLocationHeaderError;\n"],"names":[],"mappings":";;;AAKO,MAAe,qBAAqB,MAAM;AAAA,EAI/C,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AAHf;AAIF,SAAA,OAAO,KAAK,YAAY;AACxB,SAAA,gCAAgB,KAAK;AAAA,EAAA;AAE9B;AAMO,MAAM,kBAAkB,aAAa;AAAA,EAO1C,YAAY,KAAa,QAAgB,YAAoB,UAAgB;AAC3E,UAAM,QAAQ,MAAM,IAAI,UAAU,QAAQ,GAAG,EAAE;AAPxC,gCAAO;AACP;AACA;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAAA;AAAA;AAAA,EAIlB,QAAiB;AACf,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS;AAAA,EAAA;AAAA,EAG7C,QAAiB;AACf,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS;AAAA,EAAA;AAAA,EAG7C,aAAsB;AACpB,WAAO,KAAK,WAAW;AAAA,EAAA;AAAA,EAGzB,iBAA0B;AACxB,WAAO,KAAK,WAAW;AAAA,EAAA;AAAA,EAGzB,cAAuB;AACrB,WAAO,KAAK,WAAW;AAAA,EAAA;AAE3B;AAMO,MAAM,mBAAmB,aAAa;AAAA,EAM3C,YAAY,KAAa,SAAiB,MAAe,SAAe;AAChE,UAAA,gBAAgB,OAAO,EAAE;AANxB,gCAAO;AACP;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EAAA;AAEnB;AAEO,MAAM,0BAA0B,aAAa;AAAA,EAMlD,YAAY,KAAa,SAAiB,SAAe;AACjD,UAAA,gBAAgB,OAAO,EAAE;AANxB,gCAAO;AACP;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EAAA;AAEnB;AAMO,MAAM,wBAAwB,aAAa;AAAA,EAMhD,YACE,SACA,QACA,SAKA;AACA;AAAA,MACE;AAAA,OACA,mCAAS,WAAU,SAAY,EAAE,OAAO,QAAQ,UAAU;AAAA,IAC5D;AAjBO,gCAAO;AACP;AACA;AACA;AAeP,SAAK,QAAQ,mCAAS;AACtB,SAAK,SAAS;AACd,SAAK,QAAQ,mCAAS;AAAA,EAAA;AAE1B;AAEO,MAAM,+BAA+B,aAAa;AAAA,EAKvD,YAAY,UAAkB,UAAe;AACrC,UAAA,wCAAwC,QAAQ,EAAE;AALjD,gCAAO;AACP;AACA;AAIP,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAAA;AAEpB;AAEO,MAAM,iCAAiC,aAAa;AAAA,EAKzD,YAAY,UAA0C,UAAkB;AACtE,UAAM,cAAc,OAAO,aAAa,WAAW,WAAW;AAC9D,UAAM,YAAY,WAAW,4BAA4B,QAAQ,EAAE;AAN5D,gCAAO;AACP;AACA;AAKP,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAAA;AAEpB;AAEO,MAAM,mCAAmC,aAAa;AAAA,EAI3D,YAAY,SAAiB,gBAAyB;AACpD,UAAM,OAAO;AAJN,gCAAO;AACP;AAIP,SAAK,iBAAiB;AAAA,EAAA;AAE1B;AAMO,SAAS,YAAY,OAAoC;AAC9D,SAAO,iBAAiB;AAC1B;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,iBAAiB;AAC1B;AAEO,SAAS,aAAa,OAAqC;AAChE,SAAO,iBAAiB;AAC1B;AAEO,SAAS,oBACd,OAC4B;AAC5B,SAAO,iBAAiB;AAC1B;AAEO,SAAS,yBACd,OACiC;AACjC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,2BACd,OACmC;AACnC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,eAAe,OAAuC;AACpE,SAAO,iBAAiB;AAC1B;"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ export { TableOccurrence, createTableOccurrence, TableOccurrenceWithIds, createT
|
|
|
3
3
|
export { FMServerConnection } from './client/filemaker-odata.js';
|
|
4
4
|
export type { Database } from './client/database.js';
|
|
5
5
|
export type { EntitySet } from './client/entity-set.js';
|
|
6
|
-
export type {
|
|
6
|
+
export type { SchemaManager, Field, StringField, NumericField, DateField, TimeField, TimestampField, ContainerField, } from './client/schema-manager.js';
|
|
7
|
+
export type { Result, InferSchemaType, InsertData, UpdateData, ODataRecordMetadata, Metadata, } from './types.js';
|
|
7
8
|
export type { Filter, TypedFilter, FieldFilter, StringOperators, NumberOperators, BooleanOperators, DateOperators, LogicalFilter, } from './filter-types.js';
|
|
8
9
|
export { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError, } from '@fetchkit/ffetch';
|
|
9
|
-
export { FMODataError, HTTPError, ODataError, ValidationError, ResponseStructureError, RecordCountMismatchError, isHTTPError, isValidationError, isODataError, isResponseStructureError, isRecordCountMismatchError, isFMODataError, } from './errors.js';
|
|
10
|
+
export { FMODataError, HTTPError, ODataError, SchemaLockedError, ValidationError, ResponseStructureError, RecordCountMismatchError, isHTTPError, isValidationError, isODataError, isSchemaLockedError, isResponseStructureError, isRecordCountMismatchError, isFMODataError, } from './errors.js';
|
|
10
11
|
export type { FMODataErrorType } from './errors.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { BaseTable, BaseTableWithIds } from "./client/base-table.js";
|
|
|
2
2
|
import { TableOccurrence, TableOccurrenceWithIds, createTableOccurrence, createTableOccurrenceWithIds } from "./client/table-occurrence.js";
|
|
3
3
|
import { FMServerConnection } from "./client/filemaker-odata.js";
|
|
4
4
|
import { AbortError, CircuitOpenError, NetworkError, RetryLimitError, TimeoutError } from "@fetchkit/ffetch";
|
|
5
|
-
import { FMODataError, HTTPError, ODataError, RecordCountMismatchError, ResponseStructureError, ValidationError, isFMODataError, isHTTPError, isODataError, isRecordCountMismatchError, isResponseStructureError, isValidationError } from "./errors.js";
|
|
5
|
+
import { FMODataError, HTTPError, ODataError, RecordCountMismatchError, ResponseStructureError, SchemaLockedError, ValidationError, isFMODataError, isHTTPError, isODataError, isRecordCountMismatchError, isResponseStructureError, isSchemaLockedError, isValidationError } from "./errors.js";
|
|
6
6
|
export {
|
|
7
7
|
AbortError,
|
|
8
8
|
BaseTable,
|
|
@@ -16,6 +16,7 @@ export {
|
|
|
16
16
|
RecordCountMismatchError,
|
|
17
17
|
ResponseStructureError,
|
|
18
18
|
RetryLimitError,
|
|
19
|
+
SchemaLockedError,
|
|
19
20
|
TableOccurrence,
|
|
20
21
|
TableOccurrenceWithIds,
|
|
21
22
|
TimeoutError,
|
|
@@ -27,6 +28,7 @@ export {
|
|
|
27
28
|
isODataError,
|
|
28
29
|
isRecordCountMismatchError,
|
|
29
30
|
isResponseStructureError,
|
|
31
|
+
isSchemaLockedError,
|
|
30
32
|
isValidationError
|
|
31
33
|
};
|
|
32
34
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/transform.d.ts
CHANGED
|
@@ -27,6 +27,15 @@ export declare function transformFieldName(fieldName: string, baseTable: BaseTab
|
|
|
27
27
|
* @returns The FMTID or table name
|
|
28
28
|
*/
|
|
29
29
|
export declare function transformTableName(occurrence: TableOccurrence<any, any, any, any>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Gets both table name and ID from an occurrence
|
|
32
|
+
* @param occurrence - TableOccurrence instance
|
|
33
|
+
* @returns Object with name (always present) and id (may be undefined if not using IDs)
|
|
34
|
+
*/
|
|
35
|
+
export declare function getTableIdentifiers(occurrence: TableOccurrence<any, any, any, any>): {
|
|
36
|
+
name: string;
|
|
37
|
+
id: string | undefined;
|
|
38
|
+
};
|
|
30
39
|
/**
|
|
31
40
|
* Transforms response data by converting field IDs back to field names recursively.
|
|
32
41
|
* Handles both single records and arrays of records, as well as nested expand relationships.
|
package/dist/esm/transform.js
CHANGED
|
@@ -15,6 +15,12 @@ function transformFieldName(fieldName, baseTable) {
|
|
|
15
15
|
function transformTableName(occurrence) {
|
|
16
16
|
return occurrence.getTableId();
|
|
17
17
|
}
|
|
18
|
+
function getTableIdentifiers(occurrence) {
|
|
19
|
+
return {
|
|
20
|
+
name: occurrence.getTableName(),
|
|
21
|
+
id: occurrence.isUsingTableId() ? occurrence.getTableId() : void 0
|
|
22
|
+
};
|
|
23
|
+
}
|
|
18
24
|
function transformResponseFields(data, baseTable, expandConfigs) {
|
|
19
25
|
if (!baseTable.isUsingFieldIds()) {
|
|
20
26
|
return data;
|
|
@@ -97,6 +103,7 @@ function transformOrderByField(orderByString, baseTable) {
|
|
|
97
103
|
return direction ? `${fieldId} ${direction}` : fieldId;
|
|
98
104
|
}
|
|
99
105
|
export {
|
|
106
|
+
getTableIdentifiers,
|
|
100
107
|
transformFieldName,
|
|
101
108
|
transformFieldNamesArray,
|
|
102
109
|
transformFieldNamesToIds,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.js","sources":["../../src/transform.ts"],"sourcesContent":["import type { BaseTable } from \"./client/base-table\";\nimport type { TableOccurrence } from \"./client/table-occurrence\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Transforms field names to FileMaker field IDs (FMFID) in an object\n * @param data - Object with field names as keys\n * @param baseTable - BaseTable instance to get field IDs from\n * @returns Object with FMFID keys instead of field names\n */\nexport function transformFieldNamesToIds<T extends Record<string, any>>(\n data: T,\n baseTable: BaseTable<any, any, any, any>,\n): Record<string, any> {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [fieldName, value] of Object.entries(data)) {\n const fieldId = baseTable.getFieldId(fieldName as any);\n transformed[fieldId] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms FileMaker field IDs (FMFID) to field names in an object\n * @param data - Object with FMFID keys\n * @param baseTable - BaseTable instance to get field names from\n * @returns Object with field names as keys instead of FMFIDs\n */\nexport function transformFieldIdsToNames<T extends Record<string, any>>(\n data: T,\n baseTable: BaseTable<any, any, any, any>,\n): Record<string, any> {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this is an OData metadata field (starts with @)\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n \n const fieldName = baseTable.getFieldName(key);\n transformed[fieldName] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms a field name to FMFID or returns the field name if not using IDs\n * @param fieldName - The field name to transform\n * @param baseTable - BaseTable instance to get field ID from\n * @returns The FMFID or field name\n */\nexport function transformFieldName(\n fieldName: string,\n baseTable: BaseTable<any, any, any, any>,\n): string {\n return baseTable.getFieldId(fieldName as any);\n}\n\n/**\n * Transforms a table occurrence name to FMTID or returns the name if not using IDs\n * @param occurrence - TableOccurrence instance to get table ID from\n * @returns The FMTID or table name\n */\nexport function transformTableName(\n occurrence: TableOccurrence<any, any, any, any>,\n): string {\n return occurrence.getTableId();\n}\n\n/**\n * Transforms response data by converting field IDs back to field names recursively.\n * Handles both single records and arrays of records, as well as nested expand relationships.\n * \n * @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)\n * @param baseTable - BaseTable instance for the main table\n * @param expandConfigs - Configuration for expanded relations (optional)\n * @returns Transformed data with field names instead of IDs\n */\nexport function transformResponseFields(\n data: any,\n baseTable: BaseTable<any, any, any, any>,\n expandConfigs?: Array<{\n relation: string;\n occurrence?: TableOccurrence<any, any, any, any>;\n }>,\n): any {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle OData list response with value array\n if (data.value && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((record: any) =>\n transformSingleRecord(record, baseTable, expandConfigs),\n ),\n };\n }\n\n // Handle array of records\n if (Array.isArray(data)) {\n return data.map((record) =>\n transformSingleRecord(record, baseTable, expandConfigs),\n );\n }\n\n // Handle single record\n return transformSingleRecord(data, baseTable, expandConfigs);\n}\n\n/**\n * Transforms a single record, converting field IDs to names and handling nested expands\n */\nfunction transformSingleRecord(\n record: any,\n baseTable: BaseTable<any, any, any, any>,\n expandConfigs?: Array<{\n relation: string;\n occurrence?: TableOccurrence<any, any, any, any>;\n }>,\n): any {\n if (!record || typeof record !== \"object\") {\n return record;\n }\n\n const transformed: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(record)) {\n // Preserve OData metadata fields\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n // Check if this is an expanded relation (by relation name)\n let expandConfig = expandConfigs?.find((ec) => ec.relation === key);\n \n // If not found by relation name, check if this key is a FMTID\n // (FileMaker returns expanded relations with FMTID keys when using entity IDs)\n if (!expandConfig && key.startsWith(\"FMTID:\")) {\n expandConfig = expandConfigs?.find(\n (ec) =>\n ec.occurrence &&\n ec.occurrence.isUsingTableId() &&\n ec.occurrence.getTableId() === key,\n );\n }\n \n if (expandConfig && expandConfig.occurrence) {\n // Transform the expanded relation data recursively\n // Use the relation name (not the FMTID) as the key\n const relationKey = expandConfig.relation;\n \n if (Array.isArray(value)) {\n transformed[relationKey] = value.map((nestedRecord) =>\n transformSingleRecord(\n nestedRecord,\n expandConfig.occurrence!.baseTable,\n undefined, // Don't pass nested expand configs for now\n ),\n );\n } else if (value && typeof value === \"object\") {\n transformed[relationKey] = transformSingleRecord(\n value,\n expandConfig.occurrence.baseTable,\n undefined,\n );\n } else {\n transformed[relationKey] = value;\n }\n continue;\n }\n\n // Transform field ID to field name\n const fieldName = baseTable.getFieldName(key);\n transformed[fieldName] = value;\n }\n\n return transformed;\n}\n\n/**\n * Transforms an array of field names to FMFIDs\n * @param fieldNames - Array of field names\n * @param baseTable - BaseTable instance to get field IDs from\n * @returns Array of FMFIDs or field names\n */\nexport function transformFieldNamesArray(\n fieldNames: string[],\n baseTable: BaseTable<any, any, any, any>,\n): string[] {\n if (!baseTable.isUsingFieldIds()) {\n return fieldNames;\n }\n\n return fieldNames.map((fieldName) => baseTable.getFieldId(fieldName as any));\n}\n\n/**\n * Transforms a field name in an orderBy string (e.g., \"name desc\" -> \"FMFID:1 desc\")\n * @param orderByString - The orderBy string (field name with optional asc/desc)\n * @param baseTable - BaseTable instance to get field ID from\n * @returns Transformed orderBy string with FMFID\n */\nexport function transformOrderByField(\n orderByString: string,\n baseTable: BaseTable<any, any, any, any>,\n): string {\n if (!baseTable.isUsingFieldIds()) {\n return orderByString;\n }\n\n // Parse the orderBy string to extract field name and direction\n const parts = orderByString.trim().split(/\\s+/);\n const fieldName = parts[0];\n const direction = parts[1]; // \"asc\" or \"desc\" or undefined\n\n const fieldId = baseTable.getFieldId(fieldName as any);\n return direction ? `${fieldId} ${direction}` : fieldId;\n}\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transform.js","sources":["../../src/transform.ts"],"sourcesContent":["import type { BaseTable } from \"./client/base-table\";\nimport type { TableOccurrence } from \"./client/table-occurrence\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Transforms field names to FileMaker field IDs (FMFID) in an object\n * @param data - Object with field names as keys\n * @param baseTable - BaseTable instance to get field IDs from\n * @returns Object with FMFID keys instead of field names\n */\nexport function transformFieldNamesToIds<T extends Record<string, any>>(\n data: T,\n baseTable: BaseTable<any, any, any, any>,\n): Record<string, any> {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [fieldName, value] of Object.entries(data)) {\n const fieldId = baseTable.getFieldId(fieldName as any);\n transformed[fieldId] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms FileMaker field IDs (FMFID) to field names in an object\n * @param data - Object with FMFID keys\n * @param baseTable - BaseTable instance to get field names from\n * @returns Object with field names as keys instead of FMFIDs\n */\nexport function transformFieldIdsToNames<T extends Record<string, any>>(\n data: T,\n baseTable: BaseTable<any, any, any, any>,\n): Record<string, any> {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this is an OData metadata field (starts with @)\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n const fieldName = baseTable.getFieldName(key);\n transformed[fieldName] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms a field name to FMFID or returns the field name if not using IDs\n * @param fieldName - The field name to transform\n * @param baseTable - BaseTable instance to get field ID from\n * @returns The FMFID or field name\n */\nexport function transformFieldName(\n fieldName: string,\n baseTable: BaseTable<any, any, any, any>,\n): string {\n return baseTable.getFieldId(fieldName as any);\n}\n\n/**\n * Transforms a table occurrence name to FMTID or returns the name if not using IDs\n * @param occurrence - TableOccurrence instance to get table ID from\n * @returns The FMTID or table name\n */\nexport function transformTableName(\n occurrence: TableOccurrence<any, any, any, any>,\n): string {\n return occurrence.getTableId();\n}\n\n/**\n * Gets both table name and ID from an occurrence\n * @param occurrence - TableOccurrence instance\n * @returns Object with name (always present) and id (may be undefined if not using IDs)\n */\nexport function getTableIdentifiers(\n occurrence: TableOccurrence<any, any, any, any>,\n): { name: string; id: string | undefined } {\n return {\n name: occurrence.getTableName(),\n id: occurrence.isUsingTableId() ? occurrence.getTableId() : undefined,\n };\n}\n\n/**\n * Transforms response data by converting field IDs back to field names recursively.\n * Handles both single records and arrays of records, as well as nested expand relationships.\n *\n * @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)\n * @param baseTable - BaseTable instance for the main table\n * @param expandConfigs - Configuration for expanded relations (optional)\n * @returns Transformed data with field names instead of IDs\n */\nexport function transformResponseFields(\n data: any,\n baseTable: BaseTable<any, any, any, any>,\n expandConfigs?: Array<{\n relation: string;\n occurrence?: TableOccurrence<any, any, any, any>;\n }>,\n): any {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle OData list response with value array\n if (data.value && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((record: any) =>\n transformSingleRecord(record, baseTable, expandConfigs),\n ),\n };\n }\n\n // Handle array of records\n if (Array.isArray(data)) {\n return data.map((record) =>\n transformSingleRecord(record, baseTable, expandConfigs),\n );\n }\n\n // Handle single record\n return transformSingleRecord(data, baseTable, expandConfigs);\n}\n\n/**\n * Transforms a single record, converting field IDs to names and handling nested expands\n */\nfunction transformSingleRecord(\n record: any,\n baseTable: BaseTable<any, any, any, any>,\n expandConfigs?: Array<{\n relation: string;\n occurrence?: TableOccurrence<any, any, any, any>;\n }>,\n): any {\n if (!record || typeof record !== \"object\") {\n return record;\n }\n\n const transformed: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(record)) {\n // Preserve OData metadata fields\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n // Check if this is an expanded relation (by relation name)\n let expandConfig = expandConfigs?.find((ec) => ec.relation === key);\n\n // If not found by relation name, check if this key is a FMTID\n // (FileMaker returns expanded relations with FMTID keys when using entity IDs)\n if (!expandConfig && key.startsWith(\"FMTID:\")) {\n expandConfig = expandConfigs?.find(\n (ec) =>\n ec.occurrence &&\n ec.occurrence.isUsingTableId() &&\n ec.occurrence.getTableId() === key,\n );\n }\n\n if (expandConfig && expandConfig.occurrence) {\n // Transform the expanded relation data recursively\n // Use the relation name (not the FMTID) as the key\n const relationKey = expandConfig.relation;\n\n if (Array.isArray(value)) {\n transformed[relationKey] = value.map((nestedRecord) =>\n transformSingleRecord(\n nestedRecord,\n expandConfig.occurrence!.baseTable,\n undefined, // Don't pass nested expand configs for now\n ),\n );\n } else if (value && typeof value === \"object\") {\n transformed[relationKey] = transformSingleRecord(\n value,\n expandConfig.occurrence.baseTable,\n undefined,\n );\n } else {\n transformed[relationKey] = value;\n }\n continue;\n }\n\n // Transform field ID to field name\n const fieldName = baseTable.getFieldName(key);\n transformed[fieldName] = value;\n }\n\n return transformed;\n}\n\n/**\n * Transforms an array of field names to FMFIDs\n * @param fieldNames - Array of field names\n * @param baseTable - BaseTable instance to get field IDs from\n * @returns Array of FMFIDs or field names\n */\nexport function transformFieldNamesArray(\n fieldNames: string[],\n baseTable: BaseTable<any, any, any, any>,\n): string[] {\n if (!baseTable.isUsingFieldIds()) {\n return fieldNames;\n }\n\n return fieldNames.map((fieldName) => baseTable.getFieldId(fieldName as any));\n}\n\n/**\n * Transforms a field name in an orderBy string (e.g., \"name desc\" -> \"FMFID:1 desc\")\n * @param orderByString - The orderBy string (field name with optional asc/desc)\n * @param baseTable - BaseTable instance to get field ID from\n * @returns Transformed orderBy string with FMFID\n */\nexport function transformOrderByField(\n orderByString: string,\n baseTable: BaseTable<any, any, any, any>,\n): string {\n if (!baseTable.isUsingFieldIds()) {\n return orderByString;\n }\n\n // Parse the orderBy string to extract field name and direction\n const parts = orderByString.trim().split(/\\s+/);\n const fieldName = parts[0];\n const direction = parts[1]; // \"asc\" or \"desc\" or undefined\n\n const fieldId = baseTable.getFieldId(fieldName as any);\n return direction ? `${fieldId} ${direction}` : fieldId;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -13,11 +13,28 @@ export interface ExecutableBuilder<T> {
|
|
|
13
13
|
url: string;
|
|
14
14
|
body?: any;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Convert this builder to a native Request object for batch processing.
|
|
18
|
+
* @param baseUrl - The base URL for the OData service
|
|
19
|
+
* @returns A native Request object
|
|
20
|
+
*/
|
|
21
|
+
toRequest(baseUrl: string): Request;
|
|
22
|
+
/**
|
|
23
|
+
* Process a raw Response object into a typed Result.
|
|
24
|
+
* This allows builders to apply their own validation and transformation logic.
|
|
25
|
+
* @param response - The native Response object from the batch operation
|
|
26
|
+
* @param options - Optional execution options (e.g., skipValidation, includeODataAnnotations)
|
|
27
|
+
* @returns A typed Result with the builder's expected return type
|
|
28
|
+
*/
|
|
29
|
+
processResponse(response: Response, options?: ExecuteOptions): Promise<Result<T>>;
|
|
16
30
|
}
|
|
17
31
|
export interface ExecutionContext {
|
|
18
|
-
_makeRequest<T>(url: string, options?: RequestInit & FFetchOptions
|
|
32
|
+
_makeRequest<T>(url: string, options?: RequestInit & FFetchOptions & {
|
|
33
|
+
useEntityIds?: boolean;
|
|
34
|
+
}): Promise<Result<T>>;
|
|
19
35
|
_setUseEntityIds?(useEntityIds: boolean): void;
|
|
20
36
|
_getUseEntityIds?(): boolean;
|
|
37
|
+
_getBaseUrl?(): string;
|
|
21
38
|
}
|
|
22
39
|
export type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = {
|
|
23
40
|
[K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output> ? Output : never;
|
|
@@ -70,6 +87,10 @@ export type UpdateData<BT> = BT extends import('./client/base-table.js').BaseTab
|
|
|
70
87
|
export type ExecuteOptions = {
|
|
71
88
|
includeODataAnnotations?: boolean;
|
|
72
89
|
skipValidation?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Overrides the default behavior of the database to use entity IDs (rather than field names) in THIS REQUEST ONLY
|
|
92
|
+
*/
|
|
93
|
+
useEntityIds?: boolean;
|
|
73
94
|
};
|
|
74
95
|
export type ConditionallyWithODataAnnotations<T, IncludeODataAnnotations extends boolean> = IncludeODataAnnotations extends true ? T & {
|
|
75
96
|
"@id": string;
|
|
@@ -80,4 +101,51 @@ export type ExtractSchemaFromOccurrence<Occ> = Occ extends {
|
|
|
80
101
|
schema: infer S;
|
|
81
102
|
};
|
|
82
103
|
} ? S extends Record<string, StandardSchemaV1> ? S : Record<string, StandardSchemaV1> : Record<string, StandardSchemaV1>;
|
|
104
|
+
export type GenericFieldMetadata = {
|
|
105
|
+
$Nullable?: boolean;
|
|
106
|
+
"@Index"?: boolean;
|
|
107
|
+
"@Calculation"?: boolean;
|
|
108
|
+
"@Summary"?: boolean;
|
|
109
|
+
"@Global"?: boolean;
|
|
110
|
+
"@Org.OData.Core.V1.Permissions"?: "Org.OData.Core.V1.Permission@Read";
|
|
111
|
+
};
|
|
112
|
+
export type StringFieldMetadata = GenericFieldMetadata & {
|
|
113
|
+
$Type: "Edm.String";
|
|
114
|
+
$DefaultValue?: "USER" | "USERNAME" | "CURRENT_USER";
|
|
115
|
+
$MaxLength?: number;
|
|
116
|
+
};
|
|
117
|
+
export type DecimalFieldMetadata = GenericFieldMetadata & {
|
|
118
|
+
$Type: "Edm.Decimal";
|
|
119
|
+
"@AutoGenerated"?: boolean;
|
|
120
|
+
};
|
|
121
|
+
export type DateFieldMetadata = GenericFieldMetadata & {
|
|
122
|
+
$Type: "Edm.Date";
|
|
123
|
+
$DefaultValue?: "CURDATE" | "CURRENT_DATE";
|
|
124
|
+
};
|
|
125
|
+
export type TimeOfDayFieldMetadata = GenericFieldMetadata & {
|
|
126
|
+
$Type: "Edm.TimeOfDay";
|
|
127
|
+
$DefaultValue?: "CURTIME" | "CURRENT_TIME";
|
|
128
|
+
};
|
|
129
|
+
export type DateTimeOffsetFieldMetadata = GenericFieldMetadata & {
|
|
130
|
+
$Type: "Edm.Date";
|
|
131
|
+
$DefaultValue?: "CURTIMESTAMP" | "CURRENT_TIMESTAMP";
|
|
132
|
+
"@VersionId"?: boolean;
|
|
133
|
+
};
|
|
134
|
+
export type StreamFieldMetadata = {
|
|
135
|
+
$Type: "Edm.Stream";
|
|
136
|
+
$Nullable?: boolean;
|
|
137
|
+
"@EnclosedPath": string;
|
|
138
|
+
"@ExternalOpenPath": string;
|
|
139
|
+
"@ExternalSecurePath"?: string;
|
|
140
|
+
};
|
|
141
|
+
export type FieldMetadata = StringFieldMetadata | DecimalFieldMetadata | DateFieldMetadata | TimeOfDayFieldMetadata | DateTimeOffsetFieldMetadata | StreamFieldMetadata;
|
|
142
|
+
export type EntityType = {
|
|
143
|
+
$Kind: "EntityType";
|
|
144
|
+
$Key: string[];
|
|
145
|
+
} & Record<string, FieldMetadata>;
|
|
146
|
+
export type EntitySet = {
|
|
147
|
+
$Kind: "EntitySet";
|
|
148
|
+
$Type: string;
|
|
149
|
+
};
|
|
150
|
+
export type Metadata = Record<string, EntityType | EntitySet>;
|
|
83
151
|
export {};
|