@proofkit/fmodata 0.1.0-alpha.2 → 0.1.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1250 -377
- package/dist/esm/client/batch-builder.d.ts +56 -0
- package/dist/esm/client/batch-builder.js +238 -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/builders/default-select.d.ts +10 -0
- package/dist/esm/client/builders/default-select.js +43 -0
- package/dist/esm/client/builders/default-select.js.map +1 -0
- package/dist/esm/client/builders/expand-builder.d.ts +45 -0
- package/dist/esm/client/builders/expand-builder.js +174 -0
- package/dist/esm/client/builders/expand-builder.js.map +1 -0
- package/dist/esm/client/builders/index.d.ts +8 -0
- package/dist/esm/client/builders/query-string-builder.d.ts +18 -0
- package/dist/esm/client/builders/query-string-builder.js +25 -0
- package/dist/esm/client/builders/query-string-builder.js.map +1 -0
- package/dist/esm/client/builders/response-processor.d.ts +43 -0
- package/dist/esm/client/builders/response-processor.js +176 -0
- package/dist/esm/client/builders/response-processor.js.map +1 -0
- package/dist/esm/client/builders/select-mixin.d.ts +32 -0
- package/dist/esm/client/builders/select-mixin.js +30 -0
- package/dist/esm/client/builders/select-mixin.js.map +1 -0
- package/dist/esm/client/builders/select-utils.d.ts +18 -0
- package/dist/esm/client/builders/select-utils.js +23 -0
- package/dist/esm/client/builders/select-utils.js.map +1 -0
- package/dist/esm/client/builders/shared-types.d.ts +40 -0
- package/dist/esm/client/builders/table-utils.d.ts +35 -0
- package/dist/esm/client/builders/table-utils.js +45 -0
- package/dist/esm/client/builders/table-utils.js.map +1 -0
- package/dist/esm/client/database.d.ts +68 -15
- package/dist/esm/client/database.js +88 -34
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +31 -17
- package/dist/esm/client/delete-builder.js +114 -47
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +33 -27
- package/dist/esm/client/entity-set.js +123 -45
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/error-parser.d.ts +12 -0
- package/dist/esm/client/error-parser.js +30 -0
- package/dist/esm/client/error-parser.js.map +1 -0
- package/dist/esm/client/filemaker-odata.d.ts +44 -6
- package/dist/esm/client/filemaker-odata.js +172 -28
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +39 -9
- package/dist/esm/client/insert-builder.js +265 -36
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query/expand-builder.d.ts +35 -0
- package/dist/esm/client/query/index.d.ts +3 -0
- package/dist/esm/client/query/query-builder.d.ts +139 -0
- package/dist/esm/client/query/query-builder.js +481 -0
- package/dist/esm/client/query/query-builder.js.map +1 -0
- package/dist/esm/client/query/response-processor.d.ts +25 -0
- package/dist/esm/client/query/types.d.ts +77 -0
- package/dist/esm/client/query/url-builder.d.ts +71 -0
- package/dist/esm/client/query/url-builder.js +107 -0
- package/dist/esm/client/query/url-builder.js.map +1 -0
- package/dist/esm/client/query-builder.d.ts +1 -94
- package/dist/esm/client/record-builder.d.ts +107 -22
- package/dist/esm/client/record-builder.js +342 -64
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/response-processor.d.ts +33 -0
- package/dist/esm/client/sanitize-json.d.ts +35 -0
- package/dist/esm/client/sanitize-json.js +27 -0
- package/dist/esm/client/sanitize-json.js.map +1 -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 +42 -25
- package/dist/esm/client/update-builder.js +179 -46
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/client/webhook-builder.d.ts +126 -0
- package/dist/esm/client/webhook-builder.js +197 -0
- package/dist/esm/client/webhook-builder.js.map +1 -0
- package/dist/esm/errors.d.ts +90 -0
- package/dist/esm/errors.js +180 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +12 -4
- package/dist/esm/index.js +59 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger.d.ts +47 -0
- package/dist/esm/logger.js +72 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/logger.test.d.ts +1 -0
- package/dist/esm/orm/column.d.ts +62 -0
- package/dist/esm/orm/column.js +62 -0
- package/dist/esm/orm/column.js.map +1 -0
- package/dist/esm/orm/field-builders.d.ts +164 -0
- package/dist/esm/orm/field-builders.js +168 -0
- package/dist/esm/orm/field-builders.js.map +1 -0
- package/dist/esm/orm/index.d.ts +4 -0
- package/dist/esm/orm/operators.d.ts +175 -0
- package/dist/esm/orm/operators.js +242 -0
- package/dist/esm/orm/operators.js.map +1 -0
- package/dist/esm/orm/table.d.ts +355 -0
- package/dist/esm/orm/table.js +200 -0
- package/dist/esm/orm/table.js.map +1 -0
- package/dist/esm/transform.d.ts +64 -0
- package/dist/esm/transform.js +110 -0
- package/dist/esm/transform.js.map +1 -0
- package/dist/esm/types.d.ts +157 -7
- package/dist/esm/types.js +7 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/validation.d.ts +22 -9
- package/dist/esm/validation.js +195 -50
- package/dist/esm/validation.js.map +1 -1
- package/package.json +19 -4
- package/src/client/batch-builder.ts +334 -0
- package/src/client/batch-request.ts +485 -0
- package/src/client/builders/default-select.ts +80 -0
- package/src/client/builders/expand-builder.ts +245 -0
- package/src/client/builders/index.ts +11 -0
- package/src/client/builders/query-string-builder.ts +49 -0
- package/src/client/builders/response-processor.ts +286 -0
- package/src/client/builders/select-mixin.ts +75 -0
- package/src/client/builders/select-utils.ts +56 -0
- package/src/client/builders/shared-types.ts +42 -0
- package/src/client/builders/table-utils.ts +87 -0
- package/src/client/database.ts +147 -89
- package/src/client/delete-builder.ts +189 -87
- package/src/client/entity-set.ts +316 -205
- package/src/client/error-parser.ts +59 -0
- package/src/client/filemaker-odata.ts +254 -41
- package/src/client/insert-builder.ts +420 -49
- package/src/client/query/expand-builder.ts +164 -0
- package/src/client/query/index.ts +13 -0
- package/src/client/query/query-builder.ts +905 -0
- package/src/client/query/response-processor.ts +236 -0
- package/src/client/query/types.ts +128 -0
- package/src/client/query/url-builder.ts +179 -0
- package/src/client/query-builder.ts +8 -1076
- package/src/client/record-builder.ts +704 -139
- package/src/client/response-processor.ts +89 -0
- package/src/client/sanitize-json.ts +66 -0
- package/src/client/schema-manager.ts +246 -0
- package/src/client/update-builder.ts +318 -90
- package/src/client/webhook-builder.ts +285 -0
- package/src/errors.ts +261 -0
- package/src/index.ts +122 -14
- package/src/logger.test.ts +34 -0
- package/src/logger.ts +140 -0
- package/src/orm/column.ts +106 -0
- package/src/orm/field-builders.ts +318 -0
- package/src/orm/index.ts +60 -0
- package/src/orm/operators.ts +487 -0
- package/src/orm/table.ts +759 -0
- package/src/transform.ts +263 -0
- package/src/types.ts +275 -55
- package/src/validation.ts +255 -55
- package/dist/esm/client/base-table.d.ts +0 -13
- package/dist/esm/client/base-table.js +0 -19
- package/dist/esm/client/base-table.js.map +0 -1
- package/dist/esm/client/query-builder.js +0 -649
- package/dist/esm/client/query-builder.js.map +0 -1
- package/dist/esm/client/table-occurrence.d.ts +0 -25
- package/dist/esm/client/table-occurrence.js +0 -47
- package/dist/esm/client/table-occurrence.js.map +0 -1
- package/dist/esm/filter-types.d.ts +0 -76
- package/src/client/base-table.ts +0 -25
- package/src/client/table-occurrence.ts +0 -100
- package/src/filter-types.ts +0 -97
package/src/transform.ts
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type { FMTable } from "./orm/table";
|
|
2
|
+
import {
|
|
3
|
+
getBaseTableConfig,
|
|
4
|
+
getFieldId,
|
|
5
|
+
getFieldName,
|
|
6
|
+
getTableId,
|
|
7
|
+
getTableName,
|
|
8
|
+
isUsingEntityIds,
|
|
9
|
+
} from "./orm/table";
|
|
10
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Transforms field names to FileMaker field IDs (FMFID) in an object
|
|
14
|
+
* @param data - Object with field names as keys
|
|
15
|
+
* @param table - FMTable instance to get field IDs from
|
|
16
|
+
* @returns Object with FMFID keys instead of field names
|
|
17
|
+
*/
|
|
18
|
+
export function transformFieldNamesToIds<T extends Record<string, any>>(
|
|
19
|
+
data: T,
|
|
20
|
+
table: FMTable<any, any>,
|
|
21
|
+
): Record<string, any> {
|
|
22
|
+
const config = getBaseTableConfig(table);
|
|
23
|
+
if (!config.fmfIds) {
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const transformed: Record<string, any> = {};
|
|
28
|
+
for (const [fieldName, value] of Object.entries(data)) {
|
|
29
|
+
const fieldId = getFieldId(table, fieldName);
|
|
30
|
+
transformed[fieldId] = value;
|
|
31
|
+
}
|
|
32
|
+
return transformed;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Transforms FileMaker field IDs (FMFID) to field names in an object
|
|
37
|
+
* @param data - Object with FMFID keys
|
|
38
|
+
* @param table - FMTable instance to get field names from
|
|
39
|
+
* @returns Object with field names as keys instead of FMFIDs
|
|
40
|
+
*/
|
|
41
|
+
export function transformFieldIdsToNames<T extends Record<string, any>>(
|
|
42
|
+
data: T,
|
|
43
|
+
table: FMTable<any, any>,
|
|
44
|
+
): Record<string, any> {
|
|
45
|
+
const config = getBaseTableConfig(table);
|
|
46
|
+
if (!config.fmfIds) {
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const transformed: Record<string, any> = {};
|
|
51
|
+
for (const [key, value] of Object.entries(data)) {
|
|
52
|
+
// Check if this is an OData metadata field (starts with @)
|
|
53
|
+
if (key.startsWith("@")) {
|
|
54
|
+
transformed[key] = value;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const fieldName = getFieldName(table, key);
|
|
59
|
+
transformed[fieldName] = value;
|
|
60
|
+
}
|
|
61
|
+
return transformed;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Transforms a field name to FMFID or returns the field name if not using IDs
|
|
66
|
+
* @param fieldName - The field name to transform
|
|
67
|
+
* @param table - FMTable instance to get field ID from
|
|
68
|
+
* @returns The FMFID or field name
|
|
69
|
+
*/
|
|
70
|
+
export function transformFieldName(
|
|
71
|
+
fieldName: string,
|
|
72
|
+
table: FMTable<any, any>,
|
|
73
|
+
): string {
|
|
74
|
+
return getFieldId(table, fieldName);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Transforms a table name to FMTID or returns the name if not using IDs
|
|
79
|
+
* @param table - FMTable instance to get table ID from
|
|
80
|
+
* @returns The FMTID or table name
|
|
81
|
+
*/
|
|
82
|
+
export function transformTableName(table: FMTable<any, any>): string {
|
|
83
|
+
return getTableId(table);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gets both table name and ID from a table
|
|
88
|
+
* @param table - FMTable instance
|
|
89
|
+
* @returns Object with name (always present) and id (may be undefined if not using IDs)
|
|
90
|
+
*/
|
|
91
|
+
export function getTableIdentifiers(
|
|
92
|
+
table: FMTable<any, any>,
|
|
93
|
+
): { name: string; id: string | undefined } {
|
|
94
|
+
return {
|
|
95
|
+
name: getTableName(table),
|
|
96
|
+
id: isUsingEntityIds(table) ? getTableId(table) : undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Transforms response data by converting field IDs back to field names recursively.
|
|
102
|
+
* Handles both single records and arrays of records, as well as nested expand relationships.
|
|
103
|
+
*
|
|
104
|
+
* @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)
|
|
105
|
+
* @param table - FMTable instance for the main table
|
|
106
|
+
* @param expandConfigs - Configuration for expanded relations (optional)
|
|
107
|
+
* @returns Transformed data with field names instead of IDs
|
|
108
|
+
*/
|
|
109
|
+
export function transformResponseFields(
|
|
110
|
+
data: any,
|
|
111
|
+
table: FMTable<any, any>,
|
|
112
|
+
expandConfigs?: Array<{
|
|
113
|
+
relation: string;
|
|
114
|
+
table?: FMTable<any, any>;
|
|
115
|
+
}>,
|
|
116
|
+
): any {
|
|
117
|
+
const config = getBaseTableConfig(table);
|
|
118
|
+
if (!config.fmfIds) {
|
|
119
|
+
return data;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Handle null/undefined
|
|
123
|
+
if (data === null || data === undefined) {
|
|
124
|
+
return data;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Handle OData list response with value array
|
|
128
|
+
if (data.value && Array.isArray(data.value)) {
|
|
129
|
+
return {
|
|
130
|
+
...data,
|
|
131
|
+
value: data.value.map((record: any) =>
|
|
132
|
+
transformSingleRecord(record, table, expandConfigs),
|
|
133
|
+
),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Handle array of records
|
|
138
|
+
if (Array.isArray(data)) {
|
|
139
|
+
return data.map((record) =>
|
|
140
|
+
transformSingleRecord(record, table, expandConfigs),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Handle single record
|
|
145
|
+
return transformSingleRecord(data, table, expandConfigs);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Transforms a single record, converting field IDs to names and handling nested expands
|
|
150
|
+
*/
|
|
151
|
+
function transformSingleRecord(
|
|
152
|
+
record: any,
|
|
153
|
+
table: FMTable<any, any>,
|
|
154
|
+
expandConfigs?: Array<{
|
|
155
|
+
relation: string;
|
|
156
|
+
table?: FMTable<any, any>;
|
|
157
|
+
}>,
|
|
158
|
+
): any {
|
|
159
|
+
if (!record || typeof record !== "object") {
|
|
160
|
+
return record;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const transformed: Record<string, any> = {};
|
|
164
|
+
|
|
165
|
+
for (const [key, value] of Object.entries(record)) {
|
|
166
|
+
// Preserve OData metadata fields
|
|
167
|
+
if (key.startsWith("@")) {
|
|
168
|
+
transformed[key] = value;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check if this is an expanded relation (by relation name)
|
|
173
|
+
let expandConfig = expandConfigs?.find((ec) => ec.relation === key);
|
|
174
|
+
|
|
175
|
+
// If not found by relation name, check if this key is a FMTID
|
|
176
|
+
// (FileMaker returns expanded relations with FMTID keys when using entity IDs)
|
|
177
|
+
if (!expandConfig && key.startsWith("FMTID:")) {
|
|
178
|
+
expandConfig = expandConfigs?.find(
|
|
179
|
+
(ec) =>
|
|
180
|
+
ec.table && isUsingEntityIds(ec.table) && getTableId(ec.table) === key,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (expandConfig && expandConfig.table) {
|
|
185
|
+
// Transform the expanded relation data recursively
|
|
186
|
+
// Use the relation name (not the FMTID) as the key
|
|
187
|
+
const relationKey = expandConfig.relation;
|
|
188
|
+
|
|
189
|
+
if (Array.isArray(value)) {
|
|
190
|
+
transformed[relationKey] = value.map((nestedRecord) =>
|
|
191
|
+
transformSingleRecord(
|
|
192
|
+
nestedRecord,
|
|
193
|
+
expandConfig.table!,
|
|
194
|
+
undefined, // Don't pass nested expand configs for now
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
} else if (value && typeof value === "object") {
|
|
198
|
+
transformed[relationKey] = transformSingleRecord(
|
|
199
|
+
value,
|
|
200
|
+
expandConfig.table,
|
|
201
|
+
undefined,
|
|
202
|
+
);
|
|
203
|
+
} else {
|
|
204
|
+
transformed[relationKey] = value;
|
|
205
|
+
}
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Transform field ID to field name
|
|
210
|
+
const fieldName = getFieldName(table, key);
|
|
211
|
+
transformed[fieldName] = value;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return transformed;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Transforms an array of field names to FMFIDs
|
|
219
|
+
* @param fieldNames - Array of field names
|
|
220
|
+
* @param table - FMTable instance to get field IDs from
|
|
221
|
+
* @returns Array of FMFIDs or field names
|
|
222
|
+
*/
|
|
223
|
+
export function transformFieldNamesArray(
|
|
224
|
+
fieldNames: string[],
|
|
225
|
+
table: FMTable<any, any>,
|
|
226
|
+
): string[] {
|
|
227
|
+
const config = getBaseTableConfig(table);
|
|
228
|
+
if (!config.fmfIds) {
|
|
229
|
+
return fieldNames;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return fieldNames.map((fieldName) => getFieldId(table, fieldName));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Transforms a field name in an orderBy string (e.g., "name desc" -> "FMFID:1 desc")
|
|
237
|
+
* @param orderByString - The orderBy string (field name with optional asc/desc)
|
|
238
|
+
* @param table - FMTable instance to get field ID from
|
|
239
|
+
* @returns Transformed orderBy string with FMFID
|
|
240
|
+
*/
|
|
241
|
+
export function transformOrderByField(
|
|
242
|
+
orderByString: string,
|
|
243
|
+
table: FMTable<any, any> | undefined,
|
|
244
|
+
): string {
|
|
245
|
+
if (!table) {
|
|
246
|
+
return orderByString;
|
|
247
|
+
}
|
|
248
|
+
const config = getBaseTableConfig(table);
|
|
249
|
+
if (!config || !config.fmfIds) {
|
|
250
|
+
return orderByString;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Parse the orderBy string to extract field name and direction
|
|
254
|
+
const parts = orderByString.trim().split(/\s+/);
|
|
255
|
+
const fieldName = parts[0];
|
|
256
|
+
if (!fieldName) {
|
|
257
|
+
return orderByString;
|
|
258
|
+
}
|
|
259
|
+
const direction = parts[1]; // "asc" or "desc" or undefined
|
|
260
|
+
|
|
261
|
+
const fieldId = getFieldId(table, fieldName);
|
|
262
|
+
return direction ? `${fieldId} ${direction}` : fieldId;
|
|
263
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,28 +1,58 @@
|
|
|
1
1
|
import { type FFetchOptions } from "@fetchkit/ffetch";
|
|
2
|
-
import { z } from "zod/v4";
|
|
3
2
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
|
+
import type { InternalLogger } from "./logger";
|
|
4
4
|
|
|
5
5
|
export type Auth = { username: string; password: string } | { apiKey: string };
|
|
6
6
|
|
|
7
7
|
export interface ExecutableBuilder<T> {
|
|
8
8
|
execute(): Promise<Result<T>>;
|
|
9
9
|
getRequestConfig(): { method: string; url: string; body?: any };
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Convert this builder to a native Request object for batch processing.
|
|
13
|
+
* @param baseUrl - The base URL for the OData service
|
|
14
|
+
* @param options - Optional execution options (e.g., includeODataAnnotations)
|
|
15
|
+
* @returns A native Request object
|
|
16
|
+
*/
|
|
17
|
+
toRequest(baseUrl: string, options?: ExecuteOptions): Request;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Process a raw Response object into a typed Result.
|
|
21
|
+
* This allows builders to apply their own validation and transformation logic.
|
|
22
|
+
* @param response - The native Response object from the batch operation
|
|
23
|
+
* @param options - Optional execution options (e.g., skipValidation, includeODataAnnotations)
|
|
24
|
+
* @returns A typed Result with the builder's expected return type
|
|
25
|
+
*/
|
|
26
|
+
processResponse(
|
|
27
|
+
response: Response,
|
|
28
|
+
options?: ExecuteOptions,
|
|
29
|
+
): Promise<Result<T>>;
|
|
10
30
|
}
|
|
11
31
|
|
|
12
32
|
export interface ExecutionContext {
|
|
13
33
|
_makeRequest<T>(
|
|
14
34
|
url: string,
|
|
15
|
-
options?: RequestInit &
|
|
16
|
-
|
|
35
|
+
options?: RequestInit &
|
|
36
|
+
FFetchOptions & {
|
|
37
|
+
useEntityIds?: boolean;
|
|
38
|
+
includeSpecialColumns?: boolean;
|
|
39
|
+
},
|
|
40
|
+
): Promise<Result<T>>;
|
|
41
|
+
_setUseEntityIds?(useEntityIds: boolean): void;
|
|
42
|
+
_getUseEntityIds?(): boolean;
|
|
43
|
+
_setIncludeSpecialColumns?(includeSpecialColumns: boolean): void;
|
|
44
|
+
_getIncludeSpecialColumns?(): boolean;
|
|
45
|
+
_getBaseUrl?(): string;
|
|
46
|
+
_getLogger?(): InternalLogger;
|
|
17
47
|
}
|
|
18
48
|
|
|
19
49
|
export type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = {
|
|
20
|
-
[K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output>
|
|
21
|
-
? Output
|
|
50
|
+
[K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output>
|
|
51
|
+
? Output
|
|
22
52
|
: never;
|
|
23
53
|
};
|
|
24
54
|
|
|
25
|
-
export type
|
|
55
|
+
export type WithSpecialColumns<T> =
|
|
26
56
|
T extends Record<string, any>
|
|
27
57
|
? T & {
|
|
28
58
|
ROWID: number;
|
|
@@ -30,15 +60,12 @@ export type WithSystemFields<T> =
|
|
|
30
60
|
}
|
|
31
61
|
: never;
|
|
32
62
|
|
|
33
|
-
// Helper type to exclude
|
|
63
|
+
// Helper type to exclude special columns from a union of keys
|
|
34
64
|
export type ExcludeSystemFields<T extends keyof any> = Exclude<
|
|
35
65
|
T,
|
|
36
66
|
"ROWID" | "ROWMODID"
|
|
37
67
|
>;
|
|
38
68
|
|
|
39
|
-
// Helper type to omit system fields from an object type
|
|
40
|
-
export type OmitSystemFields<T> = Omit<T, "ROWID" | "ROWMODID">;
|
|
41
|
-
|
|
42
69
|
// OData record metadata fields (present on each record)
|
|
43
70
|
export type ODataRecordMetadata = {
|
|
44
71
|
"@id": string;
|
|
@@ -63,61 +90,254 @@ export type ODataFieldResponse<T> = {
|
|
|
63
90
|
};
|
|
64
91
|
|
|
65
92
|
// Result pattern for execute responses
|
|
66
|
-
export type Result<T, E =
|
|
93
|
+
export type Result<T, E = import("./errors").FMODataErrorType> =
|
|
67
94
|
| { data: T; error: undefined }
|
|
68
95
|
| { data: undefined; error: E };
|
|
69
96
|
|
|
97
|
+
// Batch operation result types
|
|
98
|
+
export type BatchItemResult<T> = {
|
|
99
|
+
data: T | undefined;
|
|
100
|
+
error: import("./errors").FMODataErrorType | undefined;
|
|
101
|
+
status: number; // HTTP status code (0 for truncated)
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export type BatchResult<T extends readonly any[]> = {
|
|
105
|
+
results: { [K in keyof T]: BatchItemResult<T[K]> };
|
|
106
|
+
successCount: number;
|
|
107
|
+
errorCount: number;
|
|
108
|
+
truncated: boolean;
|
|
109
|
+
firstErrorIndex: number | null;
|
|
110
|
+
};
|
|
111
|
+
|
|
70
112
|
// Make specific keys required, rest optional
|
|
71
113
|
export type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> &
|
|
72
114
|
Required<Pick<T, Keys>>;
|
|
73
115
|
|
|
74
|
-
// Extract
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
// Extract keys from schema where validator doesn't allow null/undefined (auto-required fields)
|
|
117
|
+
export type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> =
|
|
118
|
+
{
|
|
119
|
+
[K in keyof Schema]: Extract<
|
|
120
|
+
StandardSchemaV1.InferOutput<Schema[K]>,
|
|
121
|
+
null | undefined
|
|
122
|
+
> extends never
|
|
123
|
+
? K
|
|
124
|
+
: never;
|
|
125
|
+
}[keyof Schema];
|
|
126
|
+
|
|
127
|
+
// Helper type to compute excluded fields (readOnly fields + idField)
|
|
128
|
+
export type ExcludedFields<
|
|
129
|
+
IdField extends keyof any | undefined,
|
|
130
|
+
ReadOnly extends readonly any[],
|
|
131
|
+
> = IdField extends keyof any ? IdField | ReadOnly[number] : ReadOnly[number];
|
|
132
|
+
|
|
133
|
+
// Helper type for InsertData computation
|
|
134
|
+
type ComputeInsertData<
|
|
135
|
+
Schema extends Record<string, StandardSchemaV1>,
|
|
136
|
+
IdField extends keyof Schema | undefined,
|
|
137
|
+
Required extends readonly any[],
|
|
138
|
+
ReadOnly extends readonly any[],
|
|
139
|
+
> = [Required[number]] extends [keyof InferSchemaType<Schema>]
|
|
140
|
+
? Required extends readonly (keyof InferSchemaType<Schema>)[]
|
|
141
|
+
? MakeFieldsRequired<
|
|
142
|
+
Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,
|
|
143
|
+
Exclude<
|
|
144
|
+
AutoRequiredKeys<Schema> | Required[number],
|
|
145
|
+
ExcludedFields<IdField, ReadOnly>
|
|
146
|
+
>
|
|
147
|
+
>
|
|
148
|
+
: MakeFieldsRequired<
|
|
149
|
+
Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,
|
|
150
|
+
Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>
|
|
151
|
+
>
|
|
152
|
+
: MakeFieldsRequired<
|
|
153
|
+
Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,
|
|
154
|
+
Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>
|
|
155
|
+
>;
|
|
103
156
|
|
|
104
157
|
export type ExecuteOptions = {
|
|
105
158
|
includeODataAnnotations?: boolean;
|
|
106
159
|
skipValidation?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Overrides the default behavior of the database to use entity IDs (rather than field names) in THIS REQUEST ONLY
|
|
162
|
+
*/
|
|
163
|
+
useEntityIds?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Overrides the default behavior of the database to include special columns (ROWID and ROWMODID) in THIS REQUEST ONLY.
|
|
166
|
+
* Note: Special columns are only included when there is no $select query.
|
|
167
|
+
*/
|
|
168
|
+
includeSpecialColumns?: boolean;
|
|
107
169
|
};
|
|
108
170
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Type for the fetchHandler callback function.
|
|
173
|
+
* This is a convenience type export that matches the fetchHandler signature in FFetchOptions.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* import type { FetchHandler } from '@proofkit/fmodata';
|
|
178
|
+
*
|
|
179
|
+
* const myFetchHandler: FetchHandler = (input, init) => {
|
|
180
|
+
* console.log('Custom fetch:', input);
|
|
181
|
+
* return fetch(input, init);
|
|
182
|
+
* };
|
|
183
|
+
*
|
|
184
|
+
* await query.execute({
|
|
185
|
+
* fetchHandler: myFetchHandler
|
|
186
|
+
* });
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export type FetchHandler = (
|
|
190
|
+
input: RequestInfo | URL,
|
|
191
|
+
init?: RequestInit,
|
|
192
|
+
) => Promise<Response>;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Combined type for execute() method options.
|
|
196
|
+
*
|
|
197
|
+
* Uses FFetchOptions from @fetchkit/ffetch to ensure proper type inference.
|
|
198
|
+
* FFetchOptions is re-exported in the package to ensure type availability in consuming packages.
|
|
199
|
+
*/
|
|
200
|
+
export type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> =
|
|
201
|
+
RequestInit & FFetchOptions & ExecuteOptions & EO;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get the Accept header value based on includeODataAnnotations option
|
|
205
|
+
* @param includeODataAnnotations - Whether to include OData annotations
|
|
206
|
+
* @returns Accept header value
|
|
207
|
+
*/
|
|
208
|
+
export function getAcceptHeader(includeODataAnnotations?: boolean): string {
|
|
209
|
+
return includeODataAnnotations === true
|
|
210
|
+
? "application/json"
|
|
211
|
+
: "application/json;odata.metadata=none";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export type ConditionallyWithODataAnnotations<
|
|
215
|
+
T,
|
|
216
|
+
IncludeODataAnnotations extends boolean,
|
|
217
|
+
> = IncludeODataAnnotations extends true
|
|
218
|
+
? T & {
|
|
219
|
+
"@id": string;
|
|
220
|
+
"@editLink": string;
|
|
221
|
+
}
|
|
222
|
+
: T;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Normalizes includeSpecialColumns with a database-level default.
|
|
226
|
+
* Uses distributive conditional types to handle unions correctly.
|
|
227
|
+
* @template IncludeSpecialColumns - The includeSpecialColumns value from execute options
|
|
228
|
+
* @template DatabaseDefault - The database-level includeSpecialColumns setting (defaults to false)
|
|
229
|
+
*/
|
|
230
|
+
export type NormalizeIncludeSpecialColumns<
|
|
231
|
+
IncludeSpecialColumns extends boolean | undefined,
|
|
232
|
+
DatabaseDefault extends boolean = false,
|
|
233
|
+
> = [IncludeSpecialColumns] extends [true]
|
|
234
|
+
? true
|
|
235
|
+
: [IncludeSpecialColumns] extends [false]
|
|
236
|
+
? false
|
|
237
|
+
: DatabaseDefault; // When undefined, use database-level default
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Conditionally adds ROWID and ROWMODID special columns to a type.
|
|
241
|
+
* Special columns are only included when:
|
|
242
|
+
* - includeSpecialColumns is true AND
|
|
243
|
+
* - hasSelect is false (no $select query was applied) AND
|
|
244
|
+
* - T is an object type (not a primitive like string or number)
|
|
245
|
+
*
|
|
246
|
+
* Handles both single objects and arrays of objects.
|
|
247
|
+
*/
|
|
248
|
+
export type ConditionallyWithSpecialColumns<
|
|
249
|
+
T,
|
|
250
|
+
IncludeSpecialColumns extends boolean,
|
|
251
|
+
HasSelect extends boolean,
|
|
252
|
+
> = IncludeSpecialColumns extends true
|
|
253
|
+
? HasSelect extends false
|
|
254
|
+
? // Handle array types
|
|
255
|
+
T extends readonly (infer U)[]
|
|
256
|
+
? U extends Record<string, any>
|
|
257
|
+
? (U & {
|
|
258
|
+
ROWID: number;
|
|
259
|
+
ROWMODID: number;
|
|
260
|
+
})[]
|
|
261
|
+
: T
|
|
262
|
+
: // Handle single object types
|
|
263
|
+
T extends Record<string, any>
|
|
264
|
+
? T & {
|
|
265
|
+
ROWID: number;
|
|
266
|
+
ROWMODID: number;
|
|
267
|
+
}
|
|
268
|
+
: T // Don't add special columns to primitives (e.g., single field queries)
|
|
269
|
+
: T
|
|
270
|
+
: T;
|
|
271
|
+
|
|
272
|
+
// Helper type to extract schema from a FMTable
|
|
273
|
+
export type ExtractSchemaFromOccurrence<Occ> = Occ extends {
|
|
274
|
+
baseTable: { schema: infer S };
|
|
275
|
+
}
|
|
276
|
+
? S extends Record<string, StandardSchemaV1>
|
|
277
|
+
? S
|
|
278
|
+
: Record<string, StandardSchemaV1>
|
|
279
|
+
: Record<string, StandardSchemaV1>;
|
|
280
|
+
|
|
281
|
+
export type GenericFieldMetadata = {
|
|
282
|
+
$Nullable?: boolean;
|
|
283
|
+
"@Index"?: boolean;
|
|
284
|
+
"@Calculation"?: boolean;
|
|
285
|
+
"@Summary"?: boolean;
|
|
286
|
+
"@Global"?: boolean;
|
|
287
|
+
"@Org.OData.Core.V1.Permissions"?: "Org.OData.Core.V1.Permission@Read";
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export type StringFieldMetadata = GenericFieldMetadata & {
|
|
291
|
+
$Type: "Edm.String";
|
|
292
|
+
$DefaultValue?: "USER" | "USERNAME" | "CURRENT_USER";
|
|
293
|
+
$MaxLength?: number;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
export type DecimalFieldMetadata = GenericFieldMetadata & {
|
|
297
|
+
$Type: "Edm.Decimal";
|
|
298
|
+
"@AutoGenerated"?: boolean;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
export type DateFieldMetadata = GenericFieldMetadata & {
|
|
302
|
+
$Type: "Edm.Date";
|
|
303
|
+
$DefaultValue?: "CURDATE" | "CURRENT_DATE";
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
export type TimeOfDayFieldMetadata = GenericFieldMetadata & {
|
|
307
|
+
$Type: "Edm.TimeOfDay";
|
|
308
|
+
$DefaultValue?: "CURTIME" | "CURRENT_TIME";
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export type DateTimeOffsetFieldMetadata = GenericFieldMetadata & {
|
|
312
|
+
$Type: "Edm.Date";
|
|
313
|
+
$DefaultValue?: "CURTIMESTAMP" | "CURRENT_TIMESTAMP";
|
|
314
|
+
"@VersionId"?: boolean;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export type StreamFieldMetadata = {
|
|
318
|
+
$Type: "Edm.Stream";
|
|
319
|
+
$Nullable?: boolean;
|
|
320
|
+
"@EnclosedPath": string;
|
|
321
|
+
"@ExternalOpenPath": string;
|
|
322
|
+
"@ExternalSecurePath"?: string;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
export type FieldMetadata =
|
|
326
|
+
| StringFieldMetadata
|
|
327
|
+
| DecimalFieldMetadata
|
|
328
|
+
| DateFieldMetadata
|
|
329
|
+
| TimeOfDayFieldMetadata
|
|
330
|
+
| DateTimeOffsetFieldMetadata
|
|
331
|
+
| StreamFieldMetadata;
|
|
332
|
+
|
|
333
|
+
export type EntityType = {
|
|
334
|
+
$Kind: "EntityType";
|
|
335
|
+
$Key: string[];
|
|
336
|
+
} & Record<string, FieldMetadata>;
|
|
337
|
+
|
|
338
|
+
export type EntitySet = {
|
|
339
|
+
$Kind: "EntitySet";
|
|
340
|
+
$Type: string;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
export type Metadata = Record<string, EntityType | EntitySet>;
|