@proofkit/fmodata 0.1.0-alpha.8 → 0.1.0-beta.23
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/LICENSE.md +21 -0
- package/README.md +651 -449
- package/dist/esm/client/batch-builder.d.ts +10 -9
- package/dist/esm/client/batch-builder.js +119 -56
- package/dist/esm/client/batch-builder.js.map +1 -1
- package/dist/esm/client/batch-request.js +16 -21
- package/dist/esm/client/batch-request.js.map +1 -1
- package/dist/esm/client/builders/default-select.d.ts +10 -0
- package/dist/esm/client/builders/default-select.js +41 -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 +185 -0
- package/dist/esm/client/builders/expand-builder.js.map +1 -0
- package/dist/esm/client/builders/index.d.ts +9 -0
- package/dist/esm/client/builders/query-string-builder.d.ts +18 -0
- package/dist/esm/client/builders/query-string-builder.js +21 -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 +175 -0
- package/dist/esm/client/builders/response-processor.js.map +1 -0
- package/dist/esm/client/builders/select-mixin.d.ts +25 -0
- package/dist/esm/client/builders/select-mixin.js +28 -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 +30 -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 +44 -0
- package/dist/esm/client/builders/table-utils.js.map +1 -0
- package/dist/esm/client/database.d.ts +34 -22
- package/dist/esm/client/database.js +48 -84
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +25 -30
- package/dist/esm/client/delete-builder.js +45 -30
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +35 -43
- package/dist/esm/client/entity-set.js +110 -52
- 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 +25 -0
- package/dist/esm/client/error-parser.js.map +1 -0
- package/dist/esm/client/filemaker-odata.d.ts +26 -7
- package/dist/esm/client/filemaker-odata.js +65 -42
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +19 -24
- package/dist/esm/client/insert-builder.js +94 -58
- 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 +4 -0
- package/dist/esm/client/query/query-builder.d.ts +132 -0
- package/dist/esm/client/query/query-builder.js +456 -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 +100 -0
- package/dist/esm/client/query/url-builder.js.map +1 -0
- package/dist/esm/client/query-builder.d.ts +2 -115
- package/dist/esm/client/record-builder.d.ts +108 -36
- package/dist/esm/client/record-builder.js +284 -119
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/response-processor.d.ts +4 -9
- 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 +5 -5
- package/dist/esm/client/schema-manager.js +45 -31
- package/dist/esm/client/schema-manager.js.map +1 -1
- package/dist/esm/client/update-builder.d.ts +34 -40
- package/dist/esm/client/update-builder.js +99 -58
- 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 +189 -0
- package/dist/esm/client/webhook-builder.js.map +1 -0
- package/dist/esm/errors.d.ts +19 -2
- package/dist/esm/errors.js +39 -4
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +10 -8
- package/dist/esm/index.js +40 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger.d.ts +47 -0
- package/dist/esm/logger.js +69 -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 +63 -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 +158 -0
- package/dist/esm/orm/field-builders.js.map +1 -0
- package/dist/esm/orm/index.d.ts +5 -0
- package/dist/esm/orm/operators.d.ts +173 -0
- package/dist/esm/orm/operators.js +260 -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 +202 -0
- package/dist/esm/orm/table.js.map +1 -0
- package/dist/esm/transform.d.ts +20 -21
- package/dist/esm/transform.js +44 -45
- package/dist/esm/transform.js.map +1 -1
- package/dist/esm/types.d.ts +96 -30
- package/dist/esm/types.js +7 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/validation.d.ts +22 -12
- package/dist/esm/validation.js +132 -85
- package/dist/esm/validation.js.map +1 -1
- package/package.json +28 -20
- package/src/client/batch-builder.ts +153 -89
- package/src/client/batch-request.ts +25 -41
- package/src/client/builders/default-select.ts +75 -0
- package/src/client/builders/expand-builder.ts +246 -0
- package/src/client/builders/index.ts +11 -0
- package/src/client/builders/query-string-builder.ts +46 -0
- package/src/client/builders/response-processor.ts +279 -0
- package/src/client/builders/select-mixin.ts +65 -0
- package/src/client/builders/select-utils.ts +59 -0
- package/src/client/builders/shared-types.ts +45 -0
- package/src/client/builders/table-utils.ts +83 -0
- package/src/client/database.ts +89 -183
- package/src/client/delete-builder.ts +74 -84
- package/src/client/entity-set.ts +266 -293
- package/src/client/error-parser.ts +41 -0
- package/src/client/filemaker-odata.ts +98 -66
- package/src/client/insert-builder.ts +157 -118
- package/src/client/query/expand-builder.ts +160 -0
- package/src/client/query/index.ts +14 -0
- package/src/client/query/query-builder.ts +729 -0
- package/src/client/query/response-processor.ts +226 -0
- package/src/client/query/types.ts +126 -0
- package/src/client/query/url-builder.ts +151 -0
- package/src/client/query-builder.ts +10 -1455
- package/src/client/record-builder.ts +575 -240
- package/src/client/response-processor.ts +15 -42
- package/src/client/sanitize-json.ts +64 -0
- package/src/client/schema-manager.ts +61 -76
- package/src/client/update-builder.ts +161 -143
- package/src/client/webhook-builder.ts +265 -0
- package/src/errors.ts +49 -16
- package/src/index.ts +99 -54
- package/src/logger.test.ts +34 -0
- package/src/logger.ts +116 -0
- package/src/orm/column.ts +106 -0
- package/src/orm/field-builders.ts +250 -0
- package/src/orm/index.ts +61 -0
- package/src/orm/operators.ts +473 -0
- package/src/orm/table.ts +741 -0
- package/src/transform.ts +90 -70
- package/src/types.ts +154 -113
- package/src/validation.ts +200 -115
- package/dist/esm/client/base-table.d.ts +0 -125
- package/dist/esm/client/base-table.js +0 -57
- package/dist/esm/client/base-table.js.map +0 -1
- package/dist/esm/client/query-builder.js +0 -896
- package/dist/esm/client/query-builder.js.map +0 -1
- package/dist/esm/client/table-occurrence.d.ts +0 -72
- package/dist/esm/client/table-occurrence.js +0 -74
- 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 -166
- package/src/client/query-builder.ts.bak +0 -1457
- package/src/client/table-occurrence.ts +0 -175
- package/src/filter-types.ts +0 -97
|
@@ -1,36 +1,56 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { createLogger } from "../logger.js";
|
|
5
|
+
import { getTableName, getNavigationPaths } from "../orm/table.js";
|
|
6
|
+
import { ExpandBuilder } from "./builders/expand-builder.js";
|
|
7
|
+
import { buildSelectExpandQueryString } from "./builders/query-string-builder.js";
|
|
8
|
+
import { processODataResponse, getSchemaFromTable } from "./builders/response-processor.js";
|
|
9
|
+
import { processSelectWithRenames } from "./builders/select-mixin.js";
|
|
10
|
+
import { mergeExecuteOptions, resolveTableId, createODataRequest } from "./builders/table-utils.js";
|
|
11
|
+
import { parseErrorResponse } from "./error-parser.js";
|
|
12
|
+
import { QueryBuilder } from "./query/query-builder.js";
|
|
13
|
+
import { safeJsonParse } from "./sanitize-json.js";
|
|
7
14
|
class RecordBuilder {
|
|
8
15
|
constructor(config) {
|
|
9
|
-
__publicField(this, "
|
|
10
|
-
__publicField(this, "tableName");
|
|
16
|
+
__publicField(this, "table");
|
|
11
17
|
__publicField(this, "databaseName");
|
|
12
18
|
__publicField(this, "context");
|
|
13
19
|
__publicField(this, "recordId");
|
|
14
20
|
__publicField(this, "operation");
|
|
15
21
|
__publicField(this, "operationParam");
|
|
22
|
+
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
|
|
23
|
+
__publicField(this, "operationColumn");
|
|
16
24
|
__publicField(this, "isNavigateFromEntitySet");
|
|
17
25
|
__publicField(this, "navigateRelation");
|
|
18
26
|
__publicField(this, "navigateSourceTableName");
|
|
19
27
|
__publicField(this, "databaseUseEntityIds");
|
|
20
|
-
this
|
|
21
|
-
|
|
28
|
+
__publicField(this, "databaseIncludeSpecialColumns");
|
|
29
|
+
// Properties for select/expand support
|
|
30
|
+
__publicField(this, "selectedFields");
|
|
31
|
+
__publicField(this, "expandConfigs", []);
|
|
32
|
+
// Mapping from field names to output keys (for renamed fields in select)
|
|
33
|
+
__publicField(this, "fieldMapping");
|
|
34
|
+
// System columns requested via select() second argument
|
|
35
|
+
__publicField(this, "systemColumns");
|
|
36
|
+
__publicField(this, "logger");
|
|
37
|
+
var _a, _b;
|
|
38
|
+
this.table = config.occurrence;
|
|
22
39
|
this.databaseName = config.databaseName;
|
|
23
40
|
this.context = config.context;
|
|
24
41
|
this.recordId = config.recordId;
|
|
25
42
|
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
43
|
+
this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;
|
|
44
|
+
this.logger = ((_b = (_a = config.context) == null ? void 0 : _a._getLogger) == null ? void 0 : _b.call(_a)) ?? createLogger();
|
|
26
45
|
}
|
|
27
46
|
/**
|
|
28
|
-
* Helper to merge database-level useEntityIds with per-request options
|
|
47
|
+
* Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options
|
|
29
48
|
*/
|
|
30
49
|
mergeExecuteOptions(options) {
|
|
50
|
+
const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);
|
|
31
51
|
return {
|
|
32
|
-
...
|
|
33
|
-
|
|
52
|
+
...merged,
|
|
53
|
+
includeSpecialColumns: (options == null ? void 0 : options.includeSpecialColumns) ?? this.databaseIncludeSpecialColumns
|
|
34
54
|
};
|
|
35
55
|
}
|
|
36
56
|
/**
|
|
@@ -38,63 +58,209 @@ class RecordBuilder {
|
|
|
38
58
|
* @param useEntityIds - Optional override for entity ID usage
|
|
39
59
|
*/
|
|
40
60
|
getTableId(useEntityIds) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return this.tableName;
|
|
44
|
-
}
|
|
45
|
-
const contextDefault = ((_b = (_a = this.context)._getUseEntityIds) == null ? void 0 : _b.call(_a)) ?? false;
|
|
46
|
-
const shouldUseIds = useEntityIds ?? contextDefault;
|
|
47
|
-
if (shouldUseIds) {
|
|
48
|
-
const identifiers = getTableIdentifiers(this.occurrence);
|
|
49
|
-
if (!identifiers.id) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`useEntityIds is true but TableOccurrence "${identifiers.name}" does not have an fmtId defined`
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
return identifiers.id;
|
|
61
|
+
if (!this.table) {
|
|
62
|
+
throw new Error("Table occurrence is required");
|
|
55
63
|
}
|
|
56
|
-
return this.
|
|
64
|
+
return resolveTableId(this.table, getTableName(this.table), this.context, useEntityIds);
|
|
57
65
|
}
|
|
58
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Creates a new RecordBuilder with modified configuration.
|
|
68
|
+
* Used by select() to create new instances.
|
|
69
|
+
*/
|
|
70
|
+
cloneWithChanges(changes) {
|
|
59
71
|
const newBuilder = new RecordBuilder({
|
|
60
|
-
occurrence: this.
|
|
61
|
-
tableName: this.tableName,
|
|
72
|
+
occurrence: this.table,
|
|
62
73
|
databaseName: this.databaseName,
|
|
63
74
|
context: this.context,
|
|
64
|
-
recordId: this.recordId
|
|
75
|
+
recordId: this.recordId,
|
|
76
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
77
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
65
78
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
const mutableBuilder = newBuilder;
|
|
80
|
+
mutableBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;
|
|
81
|
+
mutableBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;
|
|
82
|
+
mutableBuilder.systemColumns = changes.systemColumns !== void 0 ? changes.systemColumns : this.systemColumns;
|
|
83
|
+
mutableBuilder.expandConfigs = [...this.expandConfigs];
|
|
84
|
+
mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
|
|
85
|
+
mutableBuilder.navigateRelation = this.navigateRelation;
|
|
86
|
+
mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;
|
|
87
|
+
mutableBuilder.operationColumn = this.operationColumn;
|
|
71
88
|
return newBuilder;
|
|
72
89
|
}
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
90
|
+
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
|
|
91
|
+
getSingleField(column) {
|
|
92
|
+
const tableName = getTableName(this.table);
|
|
93
|
+
if (!column.isFromTable(tableName)) {
|
|
94
|
+
throw new Error(`Column ${column.toString()} is not from table ${tableName}`);
|
|
95
|
+
}
|
|
96
|
+
const newBuilder = new RecordBuilder({
|
|
97
|
+
occurrence: this.table,
|
|
98
|
+
databaseName: this.databaseName,
|
|
99
|
+
context: this.context,
|
|
100
|
+
recordId: this.recordId,
|
|
101
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
102
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
103
|
+
});
|
|
104
|
+
const mutableBuilder = newBuilder;
|
|
105
|
+
mutableBuilder.operation = "getSingleField";
|
|
106
|
+
mutableBuilder.operationColumn = column;
|
|
107
|
+
mutableBuilder.operationParam = column.getFieldIdentifier(this.databaseUseEntityIds);
|
|
108
|
+
mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
|
|
109
|
+
mutableBuilder.navigateRelation = this.navigateRelation;
|
|
110
|
+
mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;
|
|
111
|
+
return newBuilder;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Select fields using column references.
|
|
115
|
+
* Allows renaming fields by using different keys in the object.
|
|
116
|
+
* Container fields cannot be selected and will cause a type error.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* db.from(contacts).get("uuid").select({
|
|
120
|
+
* name: contacts.name,
|
|
121
|
+
* userEmail: contacts.email // renamed!
|
|
122
|
+
* })
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* // Include system columns (ROWID, ROWMODID) when using select()
|
|
126
|
+
* db.from(contacts).get("uuid").select(
|
|
127
|
+
* { name: contacts.name },
|
|
128
|
+
* { ROWID: true, ROWMODID: true }
|
|
129
|
+
* )
|
|
130
|
+
*
|
|
131
|
+
* @param fields - Object mapping output keys to column references (container fields excluded)
|
|
132
|
+
* @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)
|
|
133
|
+
* @returns RecordBuilder with updated selected fields
|
|
134
|
+
*/
|
|
135
|
+
select(fields, systemColumns) {
|
|
136
|
+
const tableName = getTableName(this.table);
|
|
137
|
+
const { selectedFields, fieldMapping } = processSelectWithRenames(fields, tableName, this.logger);
|
|
138
|
+
const finalSelectedFields = [...selectedFields];
|
|
139
|
+
if (systemColumns == null ? void 0 : systemColumns.ROWID) {
|
|
140
|
+
finalSelectedFields.push("ROWID");
|
|
141
|
+
}
|
|
142
|
+
if (systemColumns == null ? void 0 : systemColumns.ROWMODID) {
|
|
143
|
+
finalSelectedFields.push("ROWMODID");
|
|
144
|
+
}
|
|
145
|
+
return this.cloneWithChanges({
|
|
146
|
+
selectedFields: finalSelectedFields,
|
|
147
|
+
fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : void 0,
|
|
148
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type parameter
|
|
149
|
+
systemColumns
|
|
150
|
+
// biome-ignore lint/suspicious/noExplicitAny: Type assertion for complex generic return type
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Expand a navigation property to include related records.
|
|
155
|
+
* Supports nested select, filter, orderBy, and expand operations.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* // Simple expand with FMTable object
|
|
160
|
+
* const contact = await db.from(contacts).get("uuid").expand(users).execute();
|
|
161
|
+
*
|
|
162
|
+
* // Expand with select
|
|
163
|
+
* const contact = await db.from(contacts).get("uuid")
|
|
164
|
+
* .expand(users, b => b.select({ username: users.username, email: users.email }))
|
|
165
|
+
* .execute();
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
expand(targetTable, callback) {
|
|
169
|
+
const newBuilder = new RecordBuilder({
|
|
170
|
+
occurrence: this.table,
|
|
171
|
+
databaseName: this.databaseName,
|
|
172
|
+
context: this.context,
|
|
173
|
+
recordId: this.recordId,
|
|
174
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
175
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
176
|
+
});
|
|
177
|
+
const mutableBuilder = newBuilder;
|
|
178
|
+
mutableBuilder.selectedFields = this.selectedFields;
|
|
179
|
+
mutableBuilder.fieldMapping = this.fieldMapping;
|
|
180
|
+
mutableBuilder.systemColumns = this.systemColumns;
|
|
181
|
+
mutableBuilder.expandConfigs = [...this.expandConfigs];
|
|
182
|
+
mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
|
|
183
|
+
mutableBuilder.navigateRelation = this.navigateRelation;
|
|
184
|
+
mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;
|
|
185
|
+
mutableBuilder.operationColumn = this.operationColumn;
|
|
186
|
+
const expandBuilder = new ExpandBuilder(this.databaseUseEntityIds, this.logger);
|
|
187
|
+
const expandConfig = expandBuilder.processExpand(
|
|
188
|
+
targetTable,
|
|
189
|
+
this.table ?? void 0,
|
|
190
|
+
callback,
|
|
191
|
+
() => (
|
|
192
|
+
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration
|
|
193
|
+
new QueryBuilder({
|
|
194
|
+
occurrence: targetTable,
|
|
195
|
+
databaseName: this.databaseName,
|
|
196
|
+
context: this.context,
|
|
197
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
198
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
199
|
+
})
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
mutableBuilder.expandConfigs.push(expandConfig);
|
|
203
|
+
return newBuilder;
|
|
204
|
+
}
|
|
205
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration
|
|
206
|
+
navigate(targetTable) {
|
|
207
|
+
const relationName = getTableName(targetTable);
|
|
208
|
+
if (this.table) {
|
|
209
|
+
const navigationPaths = getNavigationPaths(this.table);
|
|
210
|
+
if (navigationPaths && !navigationPaths.includes(relationName)) {
|
|
211
|
+
this.logger.warn(
|
|
212
|
+
`Cannot navigate to "${relationName}". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(", ") : "none"}`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
77
216
|
const builder = new QueryBuilder({
|
|
78
|
-
occurrence:
|
|
79
|
-
tableName: (targetOccurrence == null ? void 0 : targetOccurrence.name) ?? relationName,
|
|
217
|
+
occurrence: targetTable,
|
|
80
218
|
databaseName: this.databaseName,
|
|
81
|
-
context: this.context
|
|
219
|
+
context: this.context,
|
|
220
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
221
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
82
222
|
});
|
|
83
|
-
const relationId =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
builder.navigateRelation = relationId;
|
|
223
|
+
const relationId = relationName;
|
|
224
|
+
let sourceTableName;
|
|
225
|
+
let baseRelation;
|
|
87
226
|
if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {
|
|
88
|
-
|
|
89
|
-
|
|
227
|
+
sourceTableName = this.navigateSourceTableName;
|
|
228
|
+
baseRelation = this.navigateRelation;
|
|
90
229
|
} else {
|
|
91
|
-
|
|
92
|
-
|
|
230
|
+
if (!this.table) {
|
|
231
|
+
throw new Error("Table occurrence is required for navigation");
|
|
232
|
+
}
|
|
233
|
+
sourceTableName = resolveTableId(this.table, getTableName(this.table), this.context, this.databaseUseEntityIds);
|
|
93
234
|
}
|
|
235
|
+
builder.navigation = {
|
|
236
|
+
recordId: this.recordId,
|
|
237
|
+
relation: relationId,
|
|
238
|
+
sourceTableName,
|
|
239
|
+
baseRelation
|
|
240
|
+
};
|
|
241
|
+
builder.navigation = {
|
|
242
|
+
recordId: this.recordId,
|
|
243
|
+
relation: relationId,
|
|
244
|
+
sourceTableName,
|
|
245
|
+
baseRelation
|
|
246
|
+
};
|
|
94
247
|
return builder;
|
|
95
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Builds the complete query string including $select and $expand parameters.
|
|
251
|
+
*/
|
|
252
|
+
buildQueryString(includeSpecialColumns, useEntityIds) {
|
|
253
|
+
includeSpecialColumns ?? this.databaseIncludeSpecialColumns;
|
|
254
|
+
const finalUseEntityIds = useEntityIds ?? this.databaseUseEntityIds;
|
|
255
|
+
return buildSelectExpandQueryString({
|
|
256
|
+
selectedFields: this.selectedFields,
|
|
257
|
+
expandConfigs: this.expandConfigs,
|
|
258
|
+
table: this.table,
|
|
259
|
+
useEntityIds: finalUseEntityIds,
|
|
260
|
+
logger: this.logger
|
|
261
|
+
});
|
|
262
|
+
}
|
|
96
263
|
async execute(options) {
|
|
97
|
-
var _a, _b, _c;
|
|
98
264
|
let url;
|
|
99
265
|
if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {
|
|
100
266
|
url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;
|
|
@@ -102,47 +268,37 @@ class RecordBuilder {
|
|
|
102
268
|
const tableId = this.getTableId((options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds);
|
|
103
269
|
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
104
270
|
}
|
|
271
|
+
const mergedOptions = this.mergeExecuteOptions(options);
|
|
105
272
|
if (this.operation === "getSingleField" && this.operationParam) {
|
|
106
273
|
url += `/${this.operationParam}`;
|
|
274
|
+
} else {
|
|
275
|
+
const queryString = this.buildQueryString(mergedOptions.includeSpecialColumns, mergedOptions.useEntityIds);
|
|
276
|
+
url += queryString;
|
|
107
277
|
}
|
|
108
|
-
const mergedOptions = this.mergeExecuteOptions(options);
|
|
109
278
|
const result = await this.context._makeRequest(url, mergedOptions);
|
|
110
279
|
if (result.error) {
|
|
111
280
|
return { data: void 0, error: result.error };
|
|
112
281
|
}
|
|
113
|
-
|
|
282
|
+
const response = result.data;
|
|
114
283
|
if (this.operation === "getSingleField") {
|
|
115
284
|
const fieldResponse = response;
|
|
116
285
|
return { data: fieldResponse.value, error: void 0 };
|
|
117
286
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
void 0,
|
|
132
|
-
// No selected fields for record.get()
|
|
133
|
-
void 0,
|
|
134
|
-
// No expand configs
|
|
135
|
-
"exact"
|
|
136
|
-
// Expect exactly one record
|
|
137
|
-
);
|
|
138
|
-
if (!validation.valid) {
|
|
139
|
-
return { data: void 0, error: validation.error };
|
|
140
|
-
}
|
|
141
|
-
if (validation.data === null) {
|
|
142
|
-
return { data: null, error: void 0 };
|
|
143
|
-
}
|
|
144
|
-
return { data: validation.data, error: void 0 };
|
|
287
|
+
const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);
|
|
288
|
+
const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);
|
|
289
|
+
return processODataResponse(response, {
|
|
290
|
+
table: this.table,
|
|
291
|
+
schema: getSchemaFromTable(this.table),
|
|
292
|
+
singleMode: "exact",
|
|
293
|
+
selectedFields: this.selectedFields,
|
|
294
|
+
expandValidationConfigs,
|
|
295
|
+
skipValidation: options == null ? void 0 : options.skipValidation,
|
|
296
|
+
useEntityIds: mergedOptions.useEntityIds,
|
|
297
|
+
includeSpecialColumns: mergedOptions.includeSpecialColumns,
|
|
298
|
+
fieldMapping: this.fieldMapping
|
|
299
|
+
});
|
|
145
300
|
}
|
|
301
|
+
// biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value
|
|
146
302
|
getRequestConfig() {
|
|
147
303
|
let url;
|
|
148
304
|
if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {
|
|
@@ -151,60 +307,69 @@ class RecordBuilder {
|
|
|
151
307
|
const tableId = this.getTableId(this.databaseUseEntityIds);
|
|
152
308
|
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
153
309
|
}
|
|
154
|
-
if (this.operation === "getSingleField" && this.
|
|
310
|
+
if (this.operation === "getSingleField" && this.operationColumn) {
|
|
311
|
+
url += `/${this.operationColumn.getFieldIdentifier(this.databaseUseEntityIds)}`;
|
|
312
|
+
} else if (this.operation === "getSingleField" && this.operationParam) {
|
|
155
313
|
url += `/${this.operationParam}`;
|
|
314
|
+
} else {
|
|
315
|
+
const queryString = this.buildQueryString();
|
|
316
|
+
url += queryString;
|
|
156
317
|
}
|
|
157
318
|
return {
|
|
158
319
|
method: "GET",
|
|
159
320
|
url
|
|
160
321
|
};
|
|
161
322
|
}
|
|
162
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Returns the query string for this record builder (for testing purposes).
|
|
325
|
+
*/
|
|
326
|
+
getQueryString(options) {
|
|
327
|
+
const useEntityIds = (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds;
|
|
328
|
+
let path;
|
|
329
|
+
if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {
|
|
330
|
+
path = `/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;
|
|
331
|
+
} else {
|
|
332
|
+
const tableId = this.getTableId(useEntityIds);
|
|
333
|
+
path = `/${tableId}('${this.recordId}')`;
|
|
334
|
+
}
|
|
335
|
+
if (this.operation === "getSingleField" && this.operationColumn) {
|
|
336
|
+
return `${path}/${this.operationColumn.getFieldIdentifier(useEntityIds)}`;
|
|
337
|
+
}
|
|
338
|
+
if (this.operation === "getSingleField" && this.operationParam) {
|
|
339
|
+
return `${path}/${this.operationParam}`;
|
|
340
|
+
}
|
|
341
|
+
const queryString = this.buildQueryString(void 0, useEntityIds);
|
|
342
|
+
return `${path}${queryString}`;
|
|
343
|
+
}
|
|
344
|
+
toRequest(baseUrl, options) {
|
|
163
345
|
const config = this.getRequestConfig();
|
|
164
|
-
|
|
165
|
-
return new Request(fullUrl, {
|
|
166
|
-
method: config.method,
|
|
167
|
-
headers: {
|
|
168
|
-
"Content-Type": "application/json",
|
|
169
|
-
Accept: "application/json"
|
|
170
|
-
}
|
|
171
|
-
});
|
|
346
|
+
return createODataRequest(baseUrl, config, options);
|
|
172
347
|
}
|
|
173
348
|
async processResponse(response, options) {
|
|
174
|
-
|
|
175
|
-
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
const tableName = this.table ? getTableName(this.table) : "unknown";
|
|
351
|
+
const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);
|
|
352
|
+
return { data: void 0, error };
|
|
353
|
+
}
|
|
354
|
+
const rawResponse = await safeJsonParse(response);
|
|
176
355
|
if (this.operation === "getSingleField") {
|
|
177
356
|
const fieldResponse = rawResponse;
|
|
178
357
|
return { data: fieldResponse.value, error: void 0 };
|
|
179
358
|
}
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
void 0,
|
|
195
|
-
// No selected fields for record.get()
|
|
196
|
-
void 0,
|
|
197
|
-
// No expand configs
|
|
198
|
-
"exact"
|
|
199
|
-
// Expect exactly one record
|
|
200
|
-
);
|
|
201
|
-
if (!validation.valid) {
|
|
202
|
-
return { data: void 0, error: validation.error };
|
|
203
|
-
}
|
|
204
|
-
if (validation.data === null) {
|
|
205
|
-
return { data: null, error: void 0 };
|
|
206
|
-
}
|
|
207
|
-
return { data: validation.data, error: void 0 };
|
|
359
|
+
const mergedOptions = this.mergeExecuteOptions(options);
|
|
360
|
+
const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);
|
|
361
|
+
const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);
|
|
362
|
+
return processODataResponse(rawResponse, {
|
|
363
|
+
table: this.table,
|
|
364
|
+
schema: getSchemaFromTable(this.table),
|
|
365
|
+
singleMode: "exact",
|
|
366
|
+
selectedFields: this.selectedFields,
|
|
367
|
+
expandValidationConfigs,
|
|
368
|
+
skipValidation: options == null ? void 0 : options.skipValidation,
|
|
369
|
+
useEntityIds: mergedOptions.useEntityIds,
|
|
370
|
+
includeSpecialColumns: mergedOptions.includeSpecialColumns,
|
|
371
|
+
fieldMapping: this.fieldMapping
|
|
372
|
+
});
|
|
208
373
|
}
|
|
209
374
|
}
|
|
210
375
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-builder.js","sources":["../../../src/client/record-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n ODataRecordMetadata,\n ODataFieldResponse,\n InferSchemaType,\n ExecuteOptions,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport type { BaseTable } from \"./base-table\";\nimport { transformTableName, transformResponseFields, getTableIdentifiers } from \"../transform\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { validateSingleResponse } from \"../validation\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport { StandardSchemaV1 } from \"@standard-schema/spec\";\n// import type { z } from \"zod/v4\";\n\n// Helper type to extract schema from a TableOccurrence\ntype ExtractSchemaFromOccurrence<O> =\n O extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<infer S, any, any, any>\n ? S\n : never\n : never;\n\n// Helper type to extract navigation relation names from an occurrence\ntype ExtractNavigationNames<\n O extends TableOccurrence<any, any, any, any> | undefined,\n> =\n O extends TableOccurrence<any, any, infer Nav, any>\n ? Nav extends Record<string, any>\n ? keyof Nav\n : never\n : never;\n\n// Helper type to resolve a navigation item (handles both direct and lazy-loaded)\ntype ResolveNavigationItem<T> = T extends () => infer R ? R : T;\n\n// Helper type to find target occurrence by relation name\ntype FindNavigationTarget<\n O extends TableOccurrence<any, any, any, any> | undefined,\n Name extends string,\n> =\n O extends TableOccurrence<any, any, infer Nav, any>\n ? Name extends keyof Nav\n ? ResolveNavigationItem<Nav[Name]>\n : never\n : never;\n\nexport class RecordBuilder<\n T extends Record<string, any>,\n IsSingleField extends boolean = false,\n FieldKey extends keyof T = keyof T,\n Occ extends TableOccurrence<any, any, any, any> | undefined =\n | TableOccurrence<any, any, any, any>\n | undefined,\n> implements\n ExecutableBuilder<\n IsSingleField extends true ? T[FieldKey] : T & ODataRecordMetadata\n >\n{\n private occurrence?: Occ;\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private recordId: string | number;\n private operation?: \"getSingleField\" | \"navigate\";\n private operationParam?: string;\n private isNavigateFromEntitySet?: boolean;\n private navigateRelation?: string;\n private navigateSourceTableName?: string;\n\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n recordId: string | number;\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.recordId = config.recordId;\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 getSingleField<K extends keyof T>(field: K): RecordBuilder<T, true, K, Occ> {\n const newBuilder = new RecordBuilder<T, true, K, Occ>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n });\n newBuilder.operation = \"getSingleField\";\n newBuilder.operationParam = field.toString();\n // Preserve navigation context\n newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n newBuilder.navigateRelation = this.navigateRelation;\n newBuilder.navigateSourceTableName = this.navigateSourceTableName;\n return newBuilder;\n }\n\n // Overload for valid relation names - returns typed QueryBuilder\n navigate<RelationName extends ExtractNavigationNames<Occ>>(\n relationName: RelationName,\n ): QueryBuilder<\n ExtractSchemaFromOccurrence<\n FindNavigationTarget<Occ, RelationName>\n > extends Record<string, StandardSchemaV1>\n ? InferSchemaType<\n ExtractSchemaFromOccurrence<FindNavigationTarget<Occ, RelationName>>\n >\n : Record<string, any>\n >;\n // Overload for arbitrary strings - returns generic QueryBuilder with system fields\n navigate(\n relationName: string,\n ): QueryBuilder<{ ROWID: number; ROWMODID: number; [key: string]: any }>;\n // Implementation\n navigate(relationName: string): QueryBuilder<any> {\n // Use the target occurrence if available, otherwise allow untyped navigation\n // (useful when types might be incomplete)\n const targetOccurrence = this.occurrence?.navigation[relationName];\n const builder = new QueryBuilder<any>({\n occurrence: targetOccurrence,\n tableName: targetOccurrence?.name ?? relationName,\n databaseName: this.databaseName,\n context: this.context,\n });\n // Store the navigation info - we'll use it in execute\n // Transform relation name to FMTID if using entity IDs\n const relationId = targetOccurrence\n ? transformTableName(targetOccurrence)\n : relationName;\n\n (builder as any).isNavigate = true;\n (builder as any).navigateRecordId = this.recordId;\n (builder as any).navigateRelation = relationId;\n\n // If this RecordBuilder came from a navigated EntitySet, we need to preserve that base path\n if (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\n // Build the base path: /sourceTable/relation('recordId')/newRelation\n (builder as any).navigateSourceTableName = this.navigateSourceTableName;\n (builder as any).navigateBaseRelation = this.navigateRelation;\n } else {\n // Normal record navigation: /tableName('recordId')/relation\n // Transform source table name to FMTID if using entity IDs\n const sourceTableId = this.occurrence\n ? transformTableName(this.occurrence)\n : this.tableName;\n (builder as any).navigateSourceTableName = sourceTableId;\n }\n\n return builder;\n }\n\n async execute(\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<\n Result<IsSingleField extends true ? T[FieldKey] : T & ODataRecordMetadata>\n > {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Normal record: /tableName('recordId') - use FMTID if configured\n const tableId = this.getTableId(options?.useEntityIds ?? this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationParam) {\n url += `/${this.operationParam}`;\n }\n\n const mergedOptions = this.mergeExecuteOptions(options);\n const result = await this.context._makeRequest(url, mergedOptions);\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n let response = result.data;\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n const fieldResponse = response as ODataFieldResponse<T>;\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Transform response field IDs back to names if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = mergedOptions.useEntityIds ?? false;\n \n if (this.occurrence?.baseTable && shouldUseIds) {\n response = transformResponseFields(\n response,\n this.occurrence.baseTable,\n undefined, // No expand configs for simple get\n );\n }\n\n // Get schema from occurrence if available\n const schema = this.occurrence?.baseTable?.schema;\n\n // Validate the single record response\n const validation = await validateSingleResponse<any>(\n response,\n schema,\n undefined, // No selected fields for record.get()\n undefined, // No expand configs\n \"exact\", // Expect exactly one record\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Handle null response\n if (validation.data === null) {\n return { data: null as any, error: undefined };\n }\n\n return { data: validation.data, error: undefined };\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // For batch operations, use database-level setting (no per-request override available here)\n const tableId = this.getTableId(this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationParam) {\n url += `/${this.operationParam}`;\n }\n\n return {\n method: \"GET\",\n url,\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 });\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<IsSingleField extends true ? T[FieldKey] : T & ODataRecordMetadata>\n > {\n const rawResponse = await response.json();\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n const fieldResponse = rawResponse as ODataFieldResponse<T>;\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Transform response field IDs back to names if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = options?.useEntityIds ?? this.databaseUseEntityIds;\n \n let transformedResponse = rawResponse;\n if (this.occurrence?.baseTable && shouldUseIds) {\n transformedResponse = transformResponseFields(\n rawResponse,\n this.occurrence.baseTable,\n undefined, // No expand configs for simple get\n );\n }\n\n // Get schema from occurrence if available\n const schema = this.occurrence?.baseTable?.schema;\n\n // Validate the single record response\n const validation = await validateSingleResponse<any>(\n transformedResponse,\n schema,\n undefined, // No selected fields for record.get()\n undefined, // No expand configs\n \"exact\", // Expect exactly one record\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Handle null response\n if (validation.data === null) {\n return { data: null as any, error: undefined };\n }\n\n return { data: validation.data, error: undefined };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"record-builder.js","sources":["../../../src/client/record-builder.ts"],"sourcesContent":["/** biome-ignore-all lint/complexity/noBannedTypes: Empty object type represents no expands by default */\nimport type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport { createLogger, type InternalLogger } from \"../logger\";\nimport type { Column } from \"../orm/column\";\nimport type { ExtractTableName, FMTable, InferSchemaOutputFromFMTable, ValidExpandTarget } from \"../orm/table\";\nimport { getNavigationPaths, getTableName } from \"../orm/table\";\nimport type {\n ConditionallyWithODataAnnotations,\n ConditionallyWithSpecialColumns,\n ExecutableBuilder,\n ExecuteMethodOptions,\n ExecuteOptions,\n ExecutionContext,\n NormalizeIncludeSpecialColumns,\n ODataFieldResponse,\n Result,\n} from \"../types\";\nimport {\n buildSelectExpandQueryString,\n createODataRequest,\n ExpandBuilder,\n type ExpandConfig,\n type ExpandedRelations,\n getSchemaFromTable,\n mergeExecuteOptions,\n processODataResponse,\n processSelectWithRenames,\n resolveTableId,\n} from \"./builders/index\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport type { ResolveExpandedRelations, SystemColumnsFromOption, SystemColumnsOption } from \"./query/types\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { safeJsonParse } from \"./sanitize-json\";\n\n/**\n * Extract the value type from a Column.\n * This uses the phantom type stored in Column to get the actual value type.\n */\n// biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer\ntype ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;\n\n/**\n * Map a select object to its return type.\n * For each key in the select object, extract the type from the corresponding Column.\n */\ntype MapSelectToReturnType<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n TSelect extends Record<string, Column<any, any, any, any>>,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape\n _TSchema extends Record<string, any>,\n> = {\n [K in keyof TSelect]: ExtractColumnType<TSelect[K]>;\n};\n\n// Return type for RecordBuilder execute\nexport type RecordReturnType<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape\n Schema extends Record<string, any>,\n IsSingleField extends boolean,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n FieldColumn extends Column<any, any, any, any> | undefined,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration, accepts any FMTable\n Selected extends keyof Schema | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,\n Expands extends ExpandedRelations,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> = IsSingleField extends true\n ? // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer\n FieldColumn extends Column<infer TOutput, any, any, any>\n ? TOutput\n : never\n : // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n [Selected] extends [Record<string, Column<any, any, any, any>>]\n ? MapSelectToReturnType<Selected, Schema> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>\n : // Use tuple wrapping to prevent distribution over union of keys\n [Selected] extends [keyof Schema]\n ? Pick<Schema, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>\n : never;\n\nexport class RecordBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration, default allows untyped tables\n Occ extends FMTable<any, any> = FMTable<any, any>,\n IsSingleField extends boolean = false,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n FieldColumn extends Column<any, any, any, any> | undefined = undefined,\n Selected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = keyof InferSchemaOutputFromFMTable<\n NonNullable<Occ>\n >,\n Expands extends ExpandedRelations = {},\n DatabaseIncludeSpecialColumns extends boolean = false,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> implements\n ExecutableBuilder<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >\n >\n{\n private readonly table: Occ;\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly recordId: string | number;\n private readonly operation?: \"getSingleField\" | \"navigate\";\n private readonly operationParam?: string;\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n private readonly operationColumn?: Column<any, any, any, any>;\n private readonly isNavigateFromEntitySet?: boolean;\n private readonly navigateRelation?: string;\n private readonly navigateSourceTableName?: string;\n\n private readonly databaseUseEntityIds: boolean;\n private readonly databaseIncludeSpecialColumns: boolean;\n\n // Properties for select/expand support\n private readonly selectedFields?: string[];\n private readonly expandConfigs: ExpandConfig[] = [];\n // Mapping from field names to output keys (for renamed fields in select)\n private readonly fieldMapping?: Record<string, string>;\n // System columns requested via select() second argument\n private readonly systemColumns?: SystemColumnsOption;\n\n private readonly logger: InternalLogger;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n recordId: string | number;\n databaseUseEntityIds?: boolean;\n databaseIncludeSpecialColumns?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.recordId = config.recordId;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;\n this.logger = config.context?._getLogger?.() ?? createLogger();\n }\n\n /**\n * Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options\n */\n private mergeExecuteOptions(options?: RequestInit & FFetchOptions & ExecuteOptions): RequestInit &\n FFetchOptions & {\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n } {\n const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);\n return {\n ...merged,\n includeSpecialColumns: options?.includeSpecialColumns ?? this.databaseIncludeSpecialColumns,\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.table) {\n throw new Error(\"Table occurrence is required\");\n }\n return resolveTableId(this.table, getTableName(this.table), this.context, useEntityIds);\n }\n\n /**\n * Creates a new RecordBuilder with modified configuration.\n * Used by select() to create new instances.\n */\n private cloneWithChanges<\n NewSelected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = Selected,\n NewSystemCols extends SystemColumnsOption | undefined = SystemCols,\n >(changes: {\n selectedFields?: string[];\n fieldMapping?: Record<string, string>;\n systemColumns?: NewSystemCols;\n }): RecordBuilder<Occ, false, FieldColumn, NewSelected, Expands, DatabaseIncludeSpecialColumns, NewSystemCols> {\n const newBuilder = new RecordBuilder<\n Occ,\n false,\n FieldColumn,\n NewSelected,\n Expands,\n DatabaseIncludeSpecialColumns,\n NewSystemCols\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n // Use type assertion to allow assignment to readonly properties on new instance\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n mutableBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;\n mutableBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;\n mutableBuilder.systemColumns = changes.systemColumns !== undefined ? changes.systemColumns : this.systemColumns;\n mutableBuilder.expandConfigs = [...this.expandConfigs];\n // Preserve navigation context\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n mutableBuilder.operationColumn = this.operationColumn;\n return newBuilder;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n getSingleField<TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>>(\n column: TColumn,\n ): RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {},\n DatabaseIncludeSpecialColumns\n > {\n // Runtime validation: ensure column is from the correct table\n const tableName = getTableName(this.table);\n if (!column.isFromTable(tableName)) {\n throw new Error(`Column ${column.toString()} is not from table ${tableName}`);\n }\n\n const newBuilder = new RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {},\n DatabaseIncludeSpecialColumns\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n // Use type assertion to allow assignment to readonly properties on new instance\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n mutableBuilder.operation = \"getSingleField\";\n mutableBuilder.operationColumn = column;\n mutableBuilder.operationParam = column.getFieldIdentifier(this.databaseUseEntityIds);\n // Preserve navigation context\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n return newBuilder;\n }\n\n /**\n * Select fields using column references.\n * Allows renaming fields by using different keys in the object.\n * Container fields cannot be selected and will cause a type error.\n *\n * @example\n * db.from(contacts).get(\"uuid\").select({\n * name: contacts.name,\n * userEmail: contacts.email // renamed!\n * })\n *\n * @example\n * // Include system columns (ROWID, ROWMODID) when using select()\n * db.from(contacts).get(\"uuid\").select(\n * { name: contacts.name },\n * { ROWID: true, ROWMODID: true }\n * )\n *\n * @param fields - Object mapping output keys to column references (container fields excluded)\n * @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)\n * @returns RecordBuilder with updated selected fields\n */\n select<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false>>,\n TSystemCols extends SystemColumnsOption = {},\n >(\n fields: TSelect,\n systemColumns?: TSystemCols,\n ): RecordBuilder<Occ, false, FieldColumn, TSelect, Expands, DatabaseIncludeSpecialColumns, TSystemCols> {\n const tableName = getTableName(this.table);\n const { selectedFields, fieldMapping } = processSelectWithRenames(fields, tableName, this.logger);\n\n // Add system columns to selectedFields if requested\n const finalSelectedFields = [...selectedFields];\n if (systemColumns?.ROWID) {\n finalSelectedFields.push(\"ROWID\");\n }\n if (systemColumns?.ROWMODID) {\n finalSelectedFields.push(\"ROWMODID\");\n }\n\n return this.cloneWithChanges({\n selectedFields: finalSelectedFields,\n fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : undefined,\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type parameter\n systemColumns: systemColumns as any,\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for complex generic return type\n }) as any;\n }\n\n /**\n * Expand a navigation property to include related records.\n * Supports nested select, filter, orderBy, and expand operations.\n *\n * @example\n * ```typescript\n * // Simple expand with FMTable object\n * const contact = await db.from(contacts).get(\"uuid\").expand(users).execute();\n *\n * // Expand with select\n * const contact = await db.from(contacts).get(\"uuid\")\n * .expand(users, b => b.select({ username: users.username, email: users.email }))\n * .execute();\n * ```\n */\n expand<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n TargetTable extends FMTable<any, any>,\n TSelected extends\n | keyof InferSchemaOutputFromFMTable<TargetTable>\n | Record<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n Column<any, any, ExtractTableName<TargetTable>>\n > = keyof InferSchemaOutputFromFMTable<TargetTable>,\n TNestedExpands extends ExpandedRelations = {},\n >(\n targetTable: ValidExpandTarget<Occ, TargetTable>,\n callback?: (\n builder: QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}>,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n ) => QueryBuilder<TargetTable, TSelected, any, any, TNestedExpands>,\n ): RecordBuilder<\n Occ,\n false,\n FieldColumn,\n Selected,\n Expands & {\n [K in ExtractTableName<TargetTable>]: {\n schema: InferSchemaOutputFromFMTable<TargetTable>;\n selected: TSelected;\n nested: TNestedExpands;\n };\n },\n DatabaseIncludeSpecialColumns,\n SystemCols\n > {\n // Create new builder with updated types\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any ExpandedRelations\n const newBuilder = new RecordBuilder<Occ, false, FieldColumn, Selected, any, DatabaseIncludeSpecialColumns>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n\n // Use type assertion to allow assignment to readonly properties on new instance\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n // Copy existing state\n mutableBuilder.selectedFields = this.selectedFields;\n mutableBuilder.fieldMapping = this.fieldMapping;\n mutableBuilder.systemColumns = this.systemColumns;\n mutableBuilder.expandConfigs = [...this.expandConfigs];\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n mutableBuilder.operationColumn = this.operationColumn;\n\n // Use ExpandBuilder.processExpand to handle the expand logic\n const expandBuilder = new ExpandBuilder(this.databaseUseEntityIds, this.logger);\n type TargetBuilder = QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}>;\n const expandConfig = expandBuilder.processExpand<TargetTable, TargetBuilder>(\n targetTable,\n this.table ?? undefined,\n callback as ((builder: TargetBuilder) => TargetBuilder) | undefined,\n () =>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n new QueryBuilder<TargetTable, any, any, any, any, DatabaseIncludeSpecialColumns, undefined>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n }),\n );\n\n mutableBuilder.expandConfigs.push(expandConfig);\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion needed as expand changes generic parameters in complex way that TypeScript cannot infer\n return newBuilder as any;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n navigate<TargetTable extends FMTable<any, any>>(\n targetTable: ValidExpandTarget<Occ, TargetTable>,\n ): QueryBuilder<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {},\n DatabaseIncludeSpecialColumns,\n undefined\n > {\n // Extract name and validate\n const relationName = getTableName(targetTable);\n\n // Runtime validation: Check if relation name is in navigationPaths\n if (this.table) {\n const navigationPaths = getNavigationPaths(this.table);\n if (navigationPaths && !navigationPaths.includes(relationName)) {\n this.logger.warn(\n `Cannot navigate to \"${relationName}\". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(\", \") : \"none\"}`,\n );\n }\n }\n\n // Create QueryBuilder with target table\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n const builder = new QueryBuilder<TargetTable, any, any, any, any, DatabaseIncludeSpecialColumns, undefined>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n\n // Store the navigation info - we'll use it in execute\n // Use relation name as-is (entity ID handling is done in QueryBuilder)\n const relationId = relationName;\n\n // If this RecordBuilder came from a navigated EntitySet, we need to preserve that base path\n let sourceTableName: string;\n let baseRelation: string | undefined;\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // Build the base path: /sourceTable/relation('recordId')/newRelation\n sourceTableName = this.navigateSourceTableName;\n baseRelation = this.navigateRelation;\n } else {\n // Normal record navigation: /tableName('recordId')/relation\n // Use table ID if available, otherwise table name\n if (!this.table) {\n throw new Error(\"Table occurrence is required for navigation\");\n }\n sourceTableName = resolveTableId(this.table, getTableName(this.table), this.context, this.databaseUseEntityIds);\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n (builder as any).navigation = {\n recordId: this.recordId,\n relation: relationId,\n sourceTableName,\n baseRelation,\n };\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n (builder as any).navigation = {\n recordId: this.recordId,\n relation: relationId,\n sourceTableName,\n baseRelation,\n };\n\n return builder;\n }\n\n /**\n * Builds the complete query string including $select and $expand parameters.\n */\n private buildQueryString(includeSpecialColumns?: boolean, useEntityIds?: boolean): string {\n // Use merged includeSpecialColumns if provided, otherwise use database-level default\n const finalIncludeSpecialColumns = includeSpecialColumns ?? this.databaseIncludeSpecialColumns;\n // Use merged useEntityIds if provided, otherwise use database-level default\n const finalUseEntityIds = useEntityIds ?? this.databaseUseEntityIds;\n\n return buildSelectExpandQueryString({\n selectedFields: this.selectedFields,\n expandConfigs: this.expandConfigs,\n table: this.table,\n useEntityIds: finalUseEntityIds,\n logger: this.logger,\n includeSpecialColumns: finalIncludeSpecialColumns,\n });\n }\n\n async execute<EO extends ExecuteOptions>(\n options?: ExecuteMethodOptions<EO>,\n ): Promise<\n Result<\n ConditionallyWithODataAnnotations<\n ConditionallyWithSpecialColumns<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >,\n // Use the merged value: if explicitly provided in options, use that; otherwise use database default\n NormalizeIncludeSpecialColumns<EO[\"includeSpecialColumns\"], DatabaseIncludeSpecialColumns>,\n // Check if select was applied: if Selected is Record (object select) or a subset of keys, select was applied\n IsSingleField extends true\n ? false // Single field operations don't include special columns\n : // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n Selected extends Record<string, Column<any, any, any>>\n ? true\n : Selected extends keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n ? false\n : true\n >,\n EO[\"includeODataAnnotations\"] extends true ? true : false\n >\n >\n > {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Normal record: /tableName('recordId') - use FMTID if configured\n const tableId = this.getTableId(options?.useEntityIds ?? this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n const mergedOptions = this.mergeExecuteOptions(options);\n\n if (this.operation === \"getSingleField\" && this.operationParam) {\n url += `/${this.operationParam}`;\n } else {\n // Add query string for select/expand (only when not getting a single field)\n const queryString = this.buildQueryString(mergedOptions.includeSpecialColumns, mergedOptions.useEntityIds);\n url += queryString;\n }\n const result = await this.context._makeRequest(url, mergedOptions);\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n // The type is extracted from the Column stored in FieldColumn generic\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n const fieldResponse = response as ODataFieldResponse<any>;\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type extraction\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);\n\n return processODataResponse(response, {\n table: this.table,\n schema: getSchemaFromTable(this.table),\n singleMode: \"exact\",\n selectedFields: this.selectedFields,\n expandValidationConfigs,\n skipValidation: options?.skipValidation,\n useEntityIds: mergedOptions.useEntityIds,\n includeSpecialColumns: mergedOptions.includeSpecialColumns,\n fieldMapping: this.fieldMapping,\n });\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value\n getRequestConfig(): { method: string; url: string; body?: any } {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // For batch operations, use database-level setting (no per-request override available here)\n const tableId = this.getTableId(this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n // Use the column's getFieldIdentifier to support entity IDs\n url += `/${this.operationColumn.getFieldIdentifier(this.databaseUseEntityIds)}`;\n } else if (this.operation === \"getSingleField\" && this.operationParam) {\n // Fallback for backwards compatibility (shouldn't happen in normal flow)\n url += `/${this.operationParam}`;\n } else {\n // Add query string for select/expand (only when not getting a single field)\n const queryString = this.buildQueryString();\n url += queryString;\n }\n\n return {\n method: \"GET\",\n url,\n };\n }\n\n /**\n * Returns the query string for this record builder (for testing purposes).\n */\n getQueryString(options?: { useEntityIds?: boolean }): string {\n const useEntityIds = options?.useEntityIds ?? this.databaseUseEntityIds;\n let path: string;\n\n // Build the path depending on navigation context\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n path = `/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Use getTableId to respect entity ID settings (same as getRequestConfig)\n const tableId = this.getTableId(useEntityIds);\n path = `/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n return `${path}/${this.operationColumn.getFieldIdentifier(useEntityIds)}`;\n }\n if (this.operation === \"getSingleField\" && this.operationParam) {\n // Fallback for backwards compatibility (shouldn't happen in normal flow)\n return `${path}/${this.operationParam}`;\n }\n\n const queryString = this.buildQueryString(undefined, useEntityIds);\n return `${path}${queryString}`;\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): Request {\n const config = this.getRequestConfig();\n return createODataRequest(baseUrl, config, options);\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >\n >\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = this.table ? getTableName(this.table) : \"unknown\";\n const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);\n return { data: undefined, error };\n }\n\n // Use safeJsonParse to handle FileMaker's invalid JSON with unquoted ? values\n const rawResponse = await safeJsonParse(response);\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n // The type is extracted from the Column stored in FieldColumn generic\n // biome-ignore lint/suspicious/noExplicitAny: Type parameter inferred from FieldColumn generic\n const fieldResponse = rawResponse as ODataFieldResponse<any>;\n // biome-ignore lint/suspicious/noExplicitAny: Type parameter inferred from FieldColumn generic\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const mergedOptions = this.mergeExecuteOptions(options);\n const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);\n\n return processODataResponse(rawResponse, {\n table: this.table,\n schema: getSchemaFromTable(this.table),\n singleMode: \"exact\",\n selectedFields: this.selectedFields,\n expandValidationConfigs,\n skipValidation: options?.skipValidation,\n useEntityIds: mergedOptions.useEntityIds,\n includeSpecialColumns: mergedOptions.includeSpecialColumns,\n fieldMapping: this.fieldMapping,\n });\n }\n}\n"],"names":[],"mappings":"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"}
|