@proofkit/fmodata 0.1.0-alpha.19 → 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 +198 -0
- package/dist/esm/client/builders/default-select.d.ts +4 -1
- package/dist/esm/client/builders/default-select.js +3 -2
- package/dist/esm/client/builders/default-select.js.map +1 -1
- package/dist/esm/client/builders/expand-builder.js.map +1 -1
- package/dist/esm/client/builders/query-string-builder.d.ts +1 -0
- package/dist/esm/client/builders/query-string-builder.js.map +1 -1
- package/dist/esm/client/builders/response-processor.d.ts +2 -0
- package/dist/esm/client/builders/response-processor.js +8 -3
- package/dist/esm/client/builders/response-processor.js.map +1 -1
- package/dist/esm/client/database.d.ts +27 -4
- package/dist/esm/client/database.js +17 -4
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +2 -0
- package/dist/esm/client/delete-builder.js +2 -0
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +8 -7
- package/dist/esm/client/entity-set.js +21 -26
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/error-parser.js.map +1 -1
- package/dist/esm/client/filemaker-odata.d.ts +15 -2
- package/dist/esm/client/filemaker-odata.js +25 -2
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +2 -0
- package/dist/esm/client/insert-builder.js +2 -0
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query/query-builder.d.ts +26 -15
- package/dist/esm/client/query/query-builder.js +43 -9
- package/dist/esm/client/query/query-builder.js.map +1 -1
- package/dist/esm/client/query/response-processor.d.ts +1 -0
- package/dist/esm/client/query/types.d.ts +24 -3
- package/dist/esm/client/record-builder.d.ts +24 -13
- package/dist/esm/client/record-builder.js +50 -17
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/update-builder.d.ts +2 -0
- package/dist/esm/client/update-builder.js +2 -0
- 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/index.d.ts +1 -0
- package/dist/esm/orm/field-builders.d.ts +12 -2
- package/dist/esm/orm/field-builders.js +18 -2
- package/dist/esm/orm/field-builders.js.map +1 -1
- package/dist/esm/orm/table.d.ts +23 -10
- package/dist/esm/orm/table.js +17 -30
- package/dist/esm/orm/table.js.map +1 -1
- package/dist/esm/types.d.ts +32 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/validation.d.ts +5 -5
- package/dist/esm/validation.js +44 -13
- package/dist/esm/validation.js.map +1 -1
- package/package.json +2 -2
- package/src/client/builders/default-select.ts +12 -1
- package/src/client/builders/expand-builder.ts +1 -1
- package/src/client/builders/query-string-builder.ts +6 -0
- package/src/client/builders/response-processor.ts +10 -0
- package/src/client/database.ts +54 -12
- package/src/client/delete-builder.ts +5 -1
- package/src/client/entity-set.ts +72 -44
- package/src/client/error-parser.ts +3 -0
- package/src/client/filemaker-odata.ts +39 -6
- package/src/client/insert-builder.ts +4 -0
- package/src/client/query/query-builder.ts +198 -35
- package/src/client/query/response-processor.ts +15 -25
- package/src/client/query/types.ts +35 -6
- package/src/client/record-builder.ts +159 -32
- package/src/client/update-builder.ts +4 -1
- package/src/client/webhook-builder.ts +285 -0
- package/src/index.ts +6 -0
- package/src/orm/field-builders.ts +24 -2
- package/src/orm/table.ts +40 -48
- package/src/types.ts +62 -6
- package/src/validation.ts +58 -13
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ExecutionContext, ExecutableBuilder, Result, ExecuteOptions, ConditionallyWithODataAnnotations, ExecuteMethodOptions } from '../types.js';
|
|
1
|
+
import { ExecutionContext, ExecutableBuilder, Result, ExecuteOptions, ConditionallyWithODataAnnotations, ConditionallyWithSpecialColumns, NormalizeIncludeSpecialColumns, ExecuteMethodOptions } from '../types.js';
|
|
2
2
|
import { FMTable, InferSchemaOutputFromFMTable, ValidExpandTarget, ExtractTableName } from '../orm/table.js';
|
|
3
3
|
import { QueryBuilder } from './query-builder.js';
|
|
4
4
|
import { Column } from '../orm/column.js';
|
|
5
5
|
import { ExpandedRelations } from './builders/index.js';
|
|
6
|
-
import { ResolveExpandedRelations } from './query/types.js';
|
|
6
|
+
import { ResolveExpandedRelations, SystemColumnsOption, SystemColumnsFromOption } from './query/types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Extract the value type from a Column.
|
|
9
9
|
* This uses the phantom type stored in Column to get the actual value type.
|
|
@@ -16,12 +16,12 @@ type ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;
|
|
|
16
16
|
type MapSelectToReturnType<TSelect extends Record<string, Column<any, any, any, any>>, TSchema extends Record<string, any>> = {
|
|
17
17
|
[K in keyof TSelect]: ExtractColumnType<TSelect[K]>;
|
|
18
18
|
};
|
|
19
|
-
export type RecordReturnType<Schema extends Record<string, any>, IsSingleField extends boolean, FieldColumn extends Column<any, any, any, any> | undefined, Selected extends keyof Schema | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>, Expands extends ExpandedRelations> = IsSingleField extends true ? FieldColumn extends Column<infer TOutput, any, any, any> ? TOutput : never : [
|
|
19
|
+
export type RecordReturnType<Schema extends Record<string, any>, IsSingleField extends boolean, FieldColumn extends Column<any, any, any, any> | undefined, Selected extends keyof Schema | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>, Expands extends ExpandedRelations, SystemCols extends SystemColumnsOption | undefined = undefined> = IsSingleField extends true ? FieldColumn extends Column<infer TOutput, any, any, any> ? TOutput : never : [
|
|
20
20
|
Selected
|
|
21
|
-
] extends [Record<string, Column<any, any, any, any>>] ? MapSelectToReturnType<Selected, Schema> & ResolveExpandedRelations<Expands> : [
|
|
21
|
+
] extends [Record<string, Column<any, any, any, any>>] ? MapSelectToReturnType<Selected, Schema> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols> : [
|
|
22
22
|
Selected
|
|
23
|
-
] extends [keyof Schema] ? Pick<Schema, Selected> & ResolveExpandedRelations<Expands> : never;
|
|
24
|
-
export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any, any>, IsSingleField extends boolean = false, FieldColumn extends Column<any, any, any, any> | undefined = undefined, Selected extends keyof InferSchemaOutputFromFMTable<NonNullable<Occ>> | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>, Expands extends ExpandedRelations = {}> implements ExecutableBuilder<RecordReturnType<InferSchemaOutputFromFMTable<NonNullable<Occ>>, IsSingleField, FieldColumn, Selected, Expands>> {
|
|
23
|
+
] extends [keyof Schema] ? Pick<Schema, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols> : never;
|
|
24
|
+
export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any, any>, IsSingleField extends boolean = false, FieldColumn extends Column<any, any, any, any> | undefined = undefined, Selected extends keyof InferSchemaOutputFromFMTable<NonNullable<Occ>> | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>, Expands extends ExpandedRelations = {}, DatabaseIncludeSpecialColumns extends boolean = false, SystemCols extends SystemColumnsOption | undefined = undefined> implements ExecutableBuilder<RecordReturnType<InferSchemaOutputFromFMTable<NonNullable<Occ>>, IsSingleField, FieldColumn, Selected, Expands, SystemCols>> {
|
|
25
25
|
private table;
|
|
26
26
|
private databaseName;
|
|
27
27
|
private context;
|
|
@@ -33,9 +33,11 @@ export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any,
|
|
|
33
33
|
private navigateRelation?;
|
|
34
34
|
private navigateSourceTableName?;
|
|
35
35
|
private databaseUseEntityIds;
|
|
36
|
+
private databaseIncludeSpecialColumns;
|
|
36
37
|
private selectedFields?;
|
|
37
38
|
private expandConfigs;
|
|
38
39
|
private fieldMapping?;
|
|
40
|
+
private systemColumns?;
|
|
39
41
|
private logger;
|
|
40
42
|
constructor(config: {
|
|
41
43
|
occurrence: Occ;
|
|
@@ -43,9 +45,10 @@ export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any,
|
|
|
43
45
|
context: ExecutionContext;
|
|
44
46
|
recordId: string | number;
|
|
45
47
|
databaseUseEntityIds?: boolean;
|
|
48
|
+
databaseIncludeSpecialColumns?: boolean;
|
|
46
49
|
});
|
|
47
50
|
/**
|
|
48
|
-
* Helper to merge database-level useEntityIds with per-request options
|
|
51
|
+
* Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options
|
|
49
52
|
*/
|
|
50
53
|
private mergeExecuteOptions;
|
|
51
54
|
/**
|
|
@@ -58,7 +61,7 @@ export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any,
|
|
|
58
61
|
* Used by select() to create new instances.
|
|
59
62
|
*/
|
|
60
63
|
private cloneWithChanges;
|
|
61
|
-
getSingleField<TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>>(column: TColumn): RecordBuilder<Occ, true, TColumn, keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>, {}>;
|
|
64
|
+
getSingleField<TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>>(column: TColumn): RecordBuilder<Occ, true, TColumn, keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>, {}, DatabaseIncludeSpecialColumns>;
|
|
62
65
|
/**
|
|
63
66
|
* Select fields using column references.
|
|
64
67
|
* Allows renaming fields by using different keys in the object.
|
|
@@ -70,10 +73,18 @@ export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any,
|
|
|
70
73
|
* userEmail: contacts.email // renamed!
|
|
71
74
|
* })
|
|
72
75
|
*
|
|
76
|
+
* @example
|
|
77
|
+
* // Include system columns (ROWID, ROWMODID) when using select()
|
|
78
|
+
* db.from(contacts).get("uuid").select(
|
|
79
|
+
* { name: contacts.name },
|
|
80
|
+
* { ROWID: true, ROWMODID: true }
|
|
81
|
+
* )
|
|
82
|
+
*
|
|
73
83
|
* @param fields - Object mapping output keys to column references (container fields excluded)
|
|
84
|
+
* @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)
|
|
74
85
|
* @returns RecordBuilder with updated selected fields
|
|
75
86
|
*/
|
|
76
|
-
select<TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false
|
|
87
|
+
select<TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false>>, TSystemCols extends SystemColumnsOption = {}>(fields: TSelect, systemColumns?: TSystemCols): RecordBuilder<Occ, false, FieldColumn, TSelect, Expands, DatabaseIncludeSpecialColumns, TSystemCols>;
|
|
77
88
|
/**
|
|
78
89
|
* Expand a navigation property to include related records.
|
|
79
90
|
* Supports nested select, filter, orderBy, and expand operations.
|
|
@@ -95,13 +106,13 @@ export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any,
|
|
|
95
106
|
selected: TSelected;
|
|
96
107
|
nested: TNestedExpands;
|
|
97
108
|
};
|
|
98
|
-
}>;
|
|
99
|
-
navigate<TargetTable extends FMTable<any, any>>(targetTable: ValidExpandTarget<Occ, TargetTable>): QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false>;
|
|
109
|
+
}, DatabaseIncludeSpecialColumns, SystemCols>;
|
|
110
|
+
navigate<TargetTable extends FMTable<any, any>>(targetTable: ValidExpandTarget<Occ, TargetTable>): QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}, DatabaseIncludeSpecialColumns, undefined>;
|
|
100
111
|
/**
|
|
101
112
|
* Builds the complete query string including $select and $expand parameters.
|
|
102
113
|
*/
|
|
103
114
|
private buildQueryString;
|
|
104
|
-
execute<EO extends ExecuteOptions>(options?: ExecuteMethodOptions<EO>): Promise<Result<ConditionallyWithODataAnnotations<RecordReturnType<InferSchemaOutputFromFMTable<NonNullable<Occ>>, IsSingleField, FieldColumn, Selected, Expands>, EO["includeODataAnnotations"] extends true ? true : false>>>;
|
|
115
|
+
execute<EO extends ExecuteOptions>(options?: ExecuteMethodOptions<EO>): Promise<Result<ConditionallyWithODataAnnotations<ConditionallyWithSpecialColumns<RecordReturnType<InferSchemaOutputFromFMTable<NonNullable<Occ>>, IsSingleField, FieldColumn, Selected, Expands, SystemCols>, NormalizeIncludeSpecialColumns<EO["includeSpecialColumns"], DatabaseIncludeSpecialColumns>, IsSingleField extends true ? false : Selected extends Record<string, Column<any, any, any>> ? true : Selected extends keyof InferSchemaOutputFromFMTable<NonNullable<Occ>> ? false : true>, EO["includeODataAnnotations"] extends true ? true : false>>>;
|
|
105
116
|
getRequestConfig(): {
|
|
106
117
|
method: string;
|
|
107
118
|
url: string;
|
|
@@ -112,6 +123,6 @@ export declare class RecordBuilder<Occ extends FMTable<any, any> = FMTable<any,
|
|
|
112
123
|
*/
|
|
113
124
|
getQueryString(): string;
|
|
114
125
|
toRequest(baseUrl: string, options?: ExecuteOptions): Request;
|
|
115
|
-
processResponse(response: Response, options?: ExecuteOptions): Promise<Result<RecordReturnType<InferSchemaOutputFromFMTable<NonNullable<Occ>>, IsSingleField, FieldColumn, Selected, Expands>>>;
|
|
126
|
+
processResponse(response: Response, options?: ExecuteOptions): Promise<Result<RecordReturnType<InferSchemaOutputFromFMTable<NonNullable<Occ>>, IsSingleField, FieldColumn, Selected, Expands, SystemCols>>>;
|
|
116
127
|
}
|
|
117
128
|
export {};
|
|
@@ -24,11 +24,14 @@ class RecordBuilder {
|
|
|
24
24
|
__publicField(this, "navigateRelation");
|
|
25
25
|
__publicField(this, "navigateSourceTableName");
|
|
26
26
|
__publicField(this, "databaseUseEntityIds");
|
|
27
|
+
__publicField(this, "databaseIncludeSpecialColumns");
|
|
27
28
|
// Properties for select/expand support
|
|
28
29
|
__publicField(this, "selectedFields");
|
|
29
30
|
__publicField(this, "expandConfigs", []);
|
|
30
31
|
// Mapping from field names to output keys (for renamed fields in select)
|
|
31
32
|
__publicField(this, "fieldMapping");
|
|
33
|
+
// System columns requested via select() second argument
|
|
34
|
+
__publicField(this, "systemColumns");
|
|
32
35
|
__publicField(this, "logger");
|
|
33
36
|
var _a, _b;
|
|
34
37
|
this.table = config.occurrence;
|
|
@@ -36,13 +39,18 @@ class RecordBuilder {
|
|
|
36
39
|
this.context = config.context;
|
|
37
40
|
this.recordId = config.recordId;
|
|
38
41
|
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
42
|
+
this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;
|
|
39
43
|
this.logger = ((_b = (_a = config.context) == null ? void 0 : _a._getLogger) == null ? void 0 : _b.call(_a)) ?? createLogger();
|
|
40
44
|
}
|
|
41
45
|
/**
|
|
42
|
-
* Helper to merge database-level useEntityIds with per-request options
|
|
46
|
+
* Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options
|
|
43
47
|
*/
|
|
44
48
|
mergeExecuteOptions(options) {
|
|
45
|
-
|
|
49
|
+
const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);
|
|
50
|
+
return {
|
|
51
|
+
...merged,
|
|
52
|
+
includeSpecialColumns: (options == null ? void 0 : options.includeSpecialColumns) ?? this.databaseIncludeSpecialColumns
|
|
53
|
+
};
|
|
46
54
|
}
|
|
47
55
|
/**
|
|
48
56
|
* Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
|
|
@@ -69,10 +77,12 @@ class RecordBuilder {
|
|
|
69
77
|
databaseName: this.databaseName,
|
|
70
78
|
context: this.context,
|
|
71
79
|
recordId: this.recordId,
|
|
72
|
-
databaseUseEntityIds: this.databaseUseEntityIds
|
|
80
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
81
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
73
82
|
});
|
|
74
83
|
newBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;
|
|
75
84
|
newBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;
|
|
85
|
+
newBuilder.systemColumns = changes.systemColumns !== void 0 ? changes.systemColumns : this.systemColumns;
|
|
76
86
|
newBuilder.expandConfigs = [...this.expandConfigs];
|
|
77
87
|
newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
|
|
78
88
|
newBuilder.navigateRelation = this.navigateRelation;
|
|
@@ -92,7 +102,8 @@ class RecordBuilder {
|
|
|
92
102
|
databaseName: this.databaseName,
|
|
93
103
|
context: this.context,
|
|
94
104
|
recordId: this.recordId,
|
|
95
|
-
databaseUseEntityIds: this.databaseUseEntityIds
|
|
105
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
106
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
96
107
|
});
|
|
97
108
|
newBuilder.operation = "getSingleField";
|
|
98
109
|
newBuilder.operationColumn = column;
|
|
@@ -115,19 +126,35 @@ class RecordBuilder {
|
|
|
115
126
|
* userEmail: contacts.email // renamed!
|
|
116
127
|
* })
|
|
117
128
|
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Include system columns (ROWID, ROWMODID) when using select()
|
|
131
|
+
* db.from(contacts).get("uuid").select(
|
|
132
|
+
* { name: contacts.name },
|
|
133
|
+
* { ROWID: true, ROWMODID: true }
|
|
134
|
+
* )
|
|
135
|
+
*
|
|
118
136
|
* @param fields - Object mapping output keys to column references (container fields excluded)
|
|
137
|
+
* @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)
|
|
119
138
|
* @returns RecordBuilder with updated selected fields
|
|
120
139
|
*/
|
|
121
|
-
select(fields) {
|
|
140
|
+
select(fields, systemColumns) {
|
|
122
141
|
const tableName = getTableName(this.table);
|
|
123
142
|
const { selectedFields, fieldMapping } = processSelectWithRenames(
|
|
124
143
|
fields,
|
|
125
144
|
tableName,
|
|
126
145
|
this.logger
|
|
127
146
|
);
|
|
147
|
+
const finalSelectedFields = [...selectedFields];
|
|
148
|
+
if (systemColumns == null ? void 0 : systemColumns.ROWID) {
|
|
149
|
+
finalSelectedFields.push("ROWID");
|
|
150
|
+
}
|
|
151
|
+
if (systemColumns == null ? void 0 : systemColumns.ROWMODID) {
|
|
152
|
+
finalSelectedFields.push("ROWMODID");
|
|
153
|
+
}
|
|
128
154
|
return this.cloneWithChanges({
|
|
129
|
-
selectedFields,
|
|
130
|
-
fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : void 0
|
|
155
|
+
selectedFields: finalSelectedFields,
|
|
156
|
+
fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : void 0,
|
|
157
|
+
systemColumns
|
|
131
158
|
});
|
|
132
159
|
}
|
|
133
160
|
/**
|
|
@@ -151,10 +178,12 @@ class RecordBuilder {
|
|
|
151
178
|
databaseName: this.databaseName,
|
|
152
179
|
context: this.context,
|
|
153
180
|
recordId: this.recordId,
|
|
154
|
-
databaseUseEntityIds: this.databaseUseEntityIds
|
|
181
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
182
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
155
183
|
});
|
|
156
184
|
newBuilder.selectedFields = this.selectedFields;
|
|
157
185
|
newBuilder.fieldMapping = this.fieldMapping;
|
|
186
|
+
newBuilder.systemColumns = this.systemColumns;
|
|
158
187
|
newBuilder.expandConfigs = [...this.expandConfigs];
|
|
159
188
|
newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
|
|
160
189
|
newBuilder.navigateRelation = this.navigateRelation;
|
|
@@ -172,7 +201,8 @@ class RecordBuilder {
|
|
|
172
201
|
occurrence: targetTable,
|
|
173
202
|
databaseName: this.databaseName,
|
|
174
203
|
context: this.context,
|
|
175
|
-
databaseUseEntityIds: this.databaseUseEntityIds
|
|
204
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
205
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
176
206
|
})
|
|
177
207
|
);
|
|
178
208
|
newBuilder.expandConfigs.push(expandConfig);
|
|
@@ -192,7 +222,8 @@ class RecordBuilder {
|
|
|
192
222
|
occurrence: targetTable,
|
|
193
223
|
databaseName: this.databaseName,
|
|
194
224
|
context: this.context,
|
|
195
|
-
databaseUseEntityIds: this.databaseUseEntityIds
|
|
225
|
+
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
226
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns
|
|
196
227
|
});
|
|
197
228
|
const relationId = relationName;
|
|
198
229
|
let sourceTableName;
|
|
@@ -222,7 +253,8 @@ class RecordBuilder {
|
|
|
222
253
|
/**
|
|
223
254
|
* Builds the complete query string including $select and $expand parameters.
|
|
224
255
|
*/
|
|
225
|
-
buildQueryString() {
|
|
256
|
+
buildQueryString(includeSpecialColumns) {
|
|
257
|
+
includeSpecialColumns ?? this.databaseIncludeSpecialColumns;
|
|
226
258
|
return buildSelectExpandQueryString({
|
|
227
259
|
selectedFields: this.selectedFields,
|
|
228
260
|
expandConfigs: this.expandConfigs,
|
|
@@ -241,13 +273,15 @@ class RecordBuilder {
|
|
|
241
273
|
);
|
|
242
274
|
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
243
275
|
}
|
|
276
|
+
const mergedOptions = this.mergeExecuteOptions(options);
|
|
244
277
|
if (this.operation === "getSingleField" && this.operationParam) {
|
|
245
278
|
url += `/${this.operationParam}`;
|
|
246
279
|
} else {
|
|
247
|
-
const queryString = this.buildQueryString(
|
|
280
|
+
const queryString = this.buildQueryString(
|
|
281
|
+
mergedOptions.includeSpecialColumns
|
|
282
|
+
);
|
|
248
283
|
url += queryString;
|
|
249
284
|
}
|
|
250
|
-
const mergedOptions = this.mergeExecuteOptions(options);
|
|
251
285
|
const result = await this.context._makeRequest(url, mergedOptions);
|
|
252
286
|
if (result.error) {
|
|
253
287
|
return { data: void 0, error: result.error };
|
|
@@ -272,6 +306,7 @@ class RecordBuilder {
|
|
|
272
306
|
expandValidationConfigs,
|
|
273
307
|
skipValidation: options == null ? void 0 : options.skipValidation,
|
|
274
308
|
useEntityIds: mergedOptions.useEntityIds,
|
|
309
|
+
includeSpecialColumns: mergedOptions.includeSpecialColumns,
|
|
275
310
|
fieldMapping: this.fieldMapping
|
|
276
311
|
});
|
|
277
312
|
}
|
|
@@ -337,10 +372,7 @@ class RecordBuilder {
|
|
|
337
372
|
const fieldResponse = rawResponse;
|
|
338
373
|
return { data: fieldResponse.value, error: void 0 };
|
|
339
374
|
}
|
|
340
|
-
const mergedOptions = mergeExecuteOptions(
|
|
341
|
-
options,
|
|
342
|
-
this.databaseUseEntityIds
|
|
343
|
-
);
|
|
375
|
+
const mergedOptions = this.mergeExecuteOptions(options);
|
|
344
376
|
const expandBuilder = new ExpandBuilder(
|
|
345
377
|
mergedOptions.useEntityIds ?? false,
|
|
346
378
|
this.logger
|
|
@@ -356,6 +388,7 @@ class RecordBuilder {
|
|
|
356
388
|
expandValidationConfigs,
|
|
357
389
|
skipValidation: options == null ? void 0 : options.skipValidation,
|
|
358
390
|
useEntityIds: mergedOptions.useEntityIds,
|
|
391
|
+
includeSpecialColumns: mergedOptions.includeSpecialColumns,
|
|
359
392
|
fieldMapping: this.fieldMapping
|
|
360
393
|
});
|
|
361
394
|
}
|
|
@@ -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 ODataFieldResponse,\n ExecuteOptions,\n ConditionallyWithODataAnnotations,\n ExecuteMethodOptions,\n} from \"../types\";\nimport type {\n FMTable,\n InferSchemaOutputFromFMTable,\n ValidExpandTarget,\n ExtractTableName,\n ValidateNoContainerFields,\n} from \"../orm/table\";\nimport { getTableName, getNavigationPaths } from \"../orm/table\";\nimport { safeJsonParse } from \"./sanitize-json\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport { isColumn, type Column } from \"../orm/column\";\nimport {\n type ExpandConfig,\n type ExpandedRelations,\n ExpandBuilder,\n resolveTableId,\n mergeExecuteOptions,\n processODataResponse,\n getSchemaFromTable,\n processSelectWithRenames,\n buildSelectExpandQueryString,\n createODataRequest,\n} from \"./builders/index\";\nimport {\n type ResolveExpandedRelations,\n type ResolveExpandType,\n} from \"./query/types\";\nimport { createLogger, InternalLogger, Logger } from \"../logger\";\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 */\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 TSelect extends Record<string, Column<any, any, any, any>>,\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 Schema extends Record<string, any>,\n IsSingleField extends boolean,\n FieldColumn extends Column<any, any, any, any> | undefined,\n Selected extends\n | keyof Schema\n | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,\n Expands extends ExpandedRelations,\n> = IsSingleField extends true\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 [Selected] extends [Record<string, Column<any, any, any, any>>]\n ? MapSelectToReturnType<Selected, Schema> &\n ResolveExpandedRelations<Expands>\n : // Use tuple wrapping to prevent distribution over union of keys\n [Selected] extends [keyof Schema]\n ? Pick<Schema, Selected> & ResolveExpandedRelations<Expands>\n : never;\n\nexport class RecordBuilder<\n Occ extends FMTable<any, any> = FMTable<any, any>,\n IsSingleField extends boolean = false,\n FieldColumn extends Column<any, any, any, any> | undefined = undefined,\n Selected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n | Record<\n string,\n Column<any, any, ExtractTableName<NonNullable<Occ>>>\n > = keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n Expands extends ExpandedRelations = {},\n> implements\n ExecutableBuilder<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands\n >\n >\n{\n private table: Occ;\n private databaseName: string;\n private context: ExecutionContext;\n private recordId: string | number;\n private operation?: \"getSingleField\" | \"navigate\";\n private operationParam?: string;\n private operationColumn?: Column<any, any, any, any>;\n private isNavigateFromEntitySet?: boolean;\n private navigateRelation?: string;\n private navigateSourceTableName?: string;\n\n private databaseUseEntityIds: boolean;\n\n // Properties for select/expand support\n private selectedFields?: string[];\n private expandConfigs: ExpandConfig[] = [];\n // Mapping from field names to output keys (for renamed fields in select)\n private fieldMapping?: Record<string, string>;\n\n private logger: InternalLogger;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n recordId: string | number;\n databaseUseEntityIds?: 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.logger = config.context?._getLogger?.() ?? createLogger();\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 return mergeExecuteOptions(options, this.databaseUseEntityIds);\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(\n this.table,\n getTableName(this.table),\n this.context,\n useEntityIds,\n );\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 | Record<\n string,\n Column<any, any, ExtractTableName<NonNullable<Occ>>>\n > = Selected,\n >(changes: {\n selectedFields?: string[];\n fieldMapping?: Record<string, string>;\n }): RecordBuilder<Occ, false, FieldColumn, NewSelected, Expands> {\n const newBuilder = new RecordBuilder<\n Occ,\n false,\n FieldColumn,\n NewSelected,\n Expands\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n newBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;\n newBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;\n newBuilder.expandConfigs = [...this.expandConfigs];\n // Preserve navigation context\n newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n newBuilder.navigateRelation = this.navigateRelation;\n newBuilder.navigateSourceTableName = this.navigateSourceTableName;\n newBuilder.operationColumn = this.operationColumn;\n return newBuilder;\n }\n\n getSingleField<\n TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>,\n >(\n column: TColumn,\n ): RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {}\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(\n `Column ${column.toString()} is not from table ${tableName}`,\n );\n }\n\n const newBuilder = new RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {}\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n newBuilder.operation = \"getSingleField\";\n newBuilder.operationColumn = column;\n newBuilder.operationParam = column.getFieldIdentifier(\n this.databaseUseEntityIds,\n );\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 /**\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 * @param fields - Object mapping output keys to column references (container fields excluded)\n * @returns RecordBuilder with updated selected fields\n */\n select<\n TSelect extends Record<\n string,\n Column<any, any, ExtractTableName<Occ>, false>\n >,\n >(fields: TSelect): RecordBuilder<Occ, false, FieldColumn, TSelect, Expands> {\n const tableName = getTableName(this.table);\n const { selectedFields, fieldMapping } = processSelectWithRenames(\n fields,\n tableName,\n this.logger,\n );\n\n return this.cloneWithChanges({\n selectedFields,\n fieldMapping:\n Object.keys(fieldMapping).length > 0 ? fieldMapping : undefined,\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 TargetTable extends FMTable<any, any>,\n TSelected extends\n | keyof InferSchemaOutputFromFMTable<TargetTable>\n | Record<\n string,\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<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {}\n >,\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 > {\n // Create new builder with updated types\n const newBuilder = new RecordBuilder<\n Occ,\n false,\n FieldColumn,\n Selected,\n any\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n\n // Copy existing state\n newBuilder.selectedFields = this.selectedFields;\n newBuilder.fieldMapping = this.fieldMapping;\n newBuilder.expandConfigs = [...this.expandConfigs];\n newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n newBuilder.navigateRelation = this.navigateRelation;\n newBuilder.navigateSourceTableName = this.navigateSourceTableName;\n newBuilder.operationColumn = this.operationColumn;\n\n // Use ExpandBuilder.processExpand to handle the expand logic\n const expandBuilder = new ExpandBuilder(\n this.databaseUseEntityIds,\n this.logger,\n );\n type TargetBuilder = QueryBuilder<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {}\n >;\n const expandConfig = expandBuilder.processExpand<\n TargetTable,\n TargetBuilder\n >(\n targetTable,\n this.table ?? undefined,\n callback as ((builder: TargetBuilder) => TargetBuilder) | undefined,\n () =>\n new QueryBuilder<TargetTable>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n }),\n );\n\n newBuilder.expandConfigs.push(expandConfig);\n return newBuilder as any;\n }\n\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 // 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 const builder = new QueryBuilder<TargetTable>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\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 (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\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(\n this.table,\n getTableName(this.table),\n this.context,\n this.databaseUseEntityIds,\n );\n }\n\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(): string {\n return buildSelectExpandQueryString({\n selectedFields: this.selectedFields,\n expandConfigs: this.expandConfigs,\n table: this.table,\n useEntityIds: this.databaseUseEntityIds,\n logger: this.logger,\n });\n }\n\n async execute<EO extends ExecuteOptions>(\n options?: ExecuteMethodOptions<EO>,\n ): Promise<\n Result<\n ConditionallyWithODataAnnotations<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands\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 (\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(\n options?.useEntityIds ?? this.databaseUseEntityIds,\n );\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\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();\n url += queryString;\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 // The type is extracted from the Column stored in FieldColumn generic\n const fieldResponse = response as ODataFieldResponse<any>;\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const expandBuilder = new ExpandBuilder(\n mergedOptions.useEntityIds ?? false,\n this.logger,\n );\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(\n this.expandConfigs,\n );\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 fieldMapping: this.fieldMapping,\n });\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.operationColumn) {\n // Use the column's getFieldIdentifier to support entity IDs\n url += `/${this.operationColumn.getFieldIdentifier(\n this.databaseUseEntityIds,\n )}`;\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(): string {\n let path: string;\n\n // Build the path depending on navigation context\n if (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\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(this.databaseUseEntityIds);\n path = `/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n return `${path}/${this.operationColumn.getFieldIdentifier(\n this.databaseUseEntityIds,\n )}`;\n } else 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();\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 >\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(\n response,\n response.url || `/${this.databaseName}/${tableName}`,\n );\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 const fieldResponse = rawResponse as ODataFieldResponse<any>;\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const mergedOptions = mergeExecuteOptions(\n options,\n this.databaseUseEntityIds,\n );\n const expandBuilder = new ExpandBuilder(\n mergedOptions.useEntityIds ?? false,\n this.logger,\n );\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(\n this.expandConfigs,\n );\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 fieldMapping: this.fieldMapping,\n });\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AA+EO,MAAM,cAqBb;AAAA,EAsBE,YAAY,QAMT;AA3BK;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAAA;AACA,yCAAgC,CAAC;AAEjC;AAAA;AAEA;;AASN,SAAK,QAAQ,OAAO;AACpB,SAAK,eAAe,OAAO;AAC3B,SAAK,UAAU,OAAO;AACtB,SAAK,WAAW,OAAO;AAClB,SAAA,uBAAuB,OAAO,wBAAwB;AAC3D,SAAK,WAAS,kBAAO,YAAP,mBAAgB,eAAhB,gCAAkC,aAAa;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,oBACN,SAC0D;AACnD,WAAA,oBAAoB,SAAS,KAAK,oBAAoB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOvD,WAAW,cAAgC;AAC7C,QAAA,CAAC,KAAK,OAAO;AACT,YAAA,IAAI,MAAM,8BAA8B;AAAA,IAAA;AAEzC,WAAA;AAAA,MACL,KAAK;AAAA,MACL,aAAa,KAAK,KAAK;AAAA,MACvB,KAAK;AAAA,MACL;AAAA,IACF;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOM,iBAON,SAG+D;AACzD,UAAA,aAAa,IAAI,cAMrB;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AACU,eAAA,iBAAiB,QAAQ,kBAAkB,KAAK;AAChD,eAAA,eAAe,QAAQ,gBAAgB,KAAK;AACvD,eAAW,gBAAgB,CAAC,GAAG,KAAK,aAAa;AAEjD,eAAW,0BAA0B,KAAK;AAC1C,eAAW,mBAAmB,KAAK;AACnC,eAAW,0BAA0B,KAAK;AAC1C,eAAW,kBAAkB,KAAK;AAC3B,WAAA;AAAA,EAAA;AAAA,EAGT,eAGE,QAOA;AAEM,UAAA,YAAY,aAAa,KAAK,KAAK;AACzC,QAAI,CAAC,OAAO,YAAY,SAAS,GAAG;AAClC,YAAM,IAAI;AAAA,QACR,UAAU,OAAO,SAAS,CAAC,sBAAsB,SAAS;AAAA,MAC5D;AAAA,IAAA;AAGI,UAAA,aAAa,IAAI,cAMrB;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AACD,eAAW,YAAY;AACvB,eAAW,kBAAkB;AAC7B,eAAW,iBAAiB,OAAO;AAAA,MACjC,KAAK;AAAA,IACP;AAEA,eAAW,0BAA0B,KAAK;AAC1C,eAAW,mBAAmB,KAAK;AACnC,eAAW,0BAA0B,KAAK;AACnC,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBT,OAKE,QAA2E;AACrE,UAAA,YAAY,aAAa,KAAK,KAAK;AACnC,UAAA,EAAE,gBAAgB,aAAA,IAAiB;AAAA,MACvC;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IACP;AAEA,WAAO,KAAK,iBAAiB;AAAA,MAC3B;AAAA,MACA,cACE,OAAO,KAAK,YAAY,EAAE,SAAS,IAAI,eAAe;AAAA,IAAA,CACzD;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBH,OAUE,aACA,UAqBA;AAEM,UAAA,aAAa,IAAI,cAMrB;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AAGD,eAAW,iBAAiB,KAAK;AACjC,eAAW,eAAe,KAAK;AAC/B,eAAW,gBAAgB,CAAC,GAAG,KAAK,aAAa;AACjD,eAAW,0BAA0B,KAAK;AAC1C,eAAW,mBAAmB,KAAK;AACnC,eAAW,0BAA0B,KAAK;AAC1C,eAAW,kBAAkB,KAAK;AAGlC,UAAM,gBAAgB,IAAI;AAAA,MACxB,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAQA,UAAM,eAAe,cAAc;AAAA,MAIjC;AAAA,MACA,KAAK,SAAS;AAAA,MACd;AAAA,MACA,MACE,IAAI,aAA0B;AAAA,QAC5B,YAAY;AAAA,QACZ,cAAc,KAAK;AAAA,QACnB,SAAS,KAAK;AAAA,QACd,sBAAsB,KAAK;AAAA,MAC5B,CAAA;AAAA,IACL;AAEW,eAAA,cAAc,KAAK,YAAY;AACnC,WAAA;AAAA,EAAA;AAAA,EAGT,SACE,aAMA;AAEM,UAAA,eAAe,aAAa,WAAW;AAG7C,QAAI,KAAK,OAAO;AACR,YAAA,kBAAkB,mBAAmB,KAAK,KAAK;AACrD,UAAI,mBAAmB,CAAC,gBAAgB,SAAS,YAAY,GAAG;AAC9D,aAAK,OAAO;AAAA,UACV,uBAAuB,YAAY,8BAA8B,gBAAgB,SAAS,IAAI,gBAAgB,KAAK,IAAI,IAAI,MAAM;AAAA,QACnI;AAAA,MAAA;AAAA,IACF;AAII,UAAA,UAAU,IAAI,aAA0B;AAAA,MAC5C,YAAY;AAAA,MACZ,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AAID,UAAM,aAAa;AAGf,QAAA;AACA,QAAA;AACJ,QACE,KAAK,2BACL,KAAK,2BACL,KAAK,kBACL;AAEA,wBAAkB,KAAK;AACvB,qBAAe,KAAK;AAAA,IAAA,OACf;AAGD,UAAA,CAAC,KAAK,OAAO;AACT,cAAA,IAAI,MAAM,6CAA6C;AAAA,MAAA;AAE7C,wBAAA;AAAA,QAChB,KAAK;AAAA,QACL,aAAa,KAAK,KAAK;AAAA,QACvB,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AAAA,IAAA;AAGD,YAAgB,aAAa;AAAA,MAC5B,UAAU,KAAK;AAAA,MACf,UAAU;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAEO,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMD,mBAA2B;AACjC,WAAO,6BAA6B;AAAA,MAClC,gBAAgB,KAAK;AAAA,MACrB,eAAe,KAAK;AAAA,MACpB,OAAO,KAAK;AAAA,MACZ,cAAc,KAAK;AAAA,MACnB,QAAQ,KAAK;AAAA,IAAA,CACd;AAAA,EAAA;AAAA,EAGH,MAAM,QACJ,SAcA;AACI,QAAA;AAGJ,QACE,KAAK,2BACL,KAAK,2BACL,KAAK,kBACL;AAEM,YAAA,IAAI,KAAK,YAAY,IAAI,KAAK,uBAAuB,IAAI,KAAK,gBAAgB,KAAK,KAAK,QAAQ;AAAA,IAAA,OACjG;AAEL,YAAM,UAAU,KAAK;AAAA,SACnB,mCAAS,iBAAgB,KAAK;AAAA,MAChC;AACA,YAAM,IAAI,KAAK,YAAY,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAA,IAAA;AAG1D,QAAI,KAAK,cAAc,oBAAoB,KAAK,gBAAgB;AACvD,aAAA,IAAI,KAAK,cAAc;AAAA,IAAA,OACzB;AAEC,YAAA,cAAc,KAAK,iBAAiB;AACnC,aAAA;AAAA,IAAA;AAGH,UAAA,gBAAgB,KAAK,oBAAoB,OAAO;AACtD,UAAM,SAAS,MAAM,KAAK,QAAQ,aAAa,KAAK,aAAa;AAEjE,QAAI,OAAO,OAAO;AAChB,aAAO,EAAE,MAAM,QAAW,OAAO,OAAO,MAAM;AAAA,IAAA;AAGhD,QAAI,WAAW,OAAO;AAGlB,QAAA,KAAK,cAAc,kBAAkB;AAGvC,YAAM,gBAAgB;AACtB,aAAO,EAAE,MAAM,cAAc,OAAc,OAAO,OAAU;AAAA,IAAA;AAI9D,UAAM,gBAAgB,IAAI;AAAA,MACxB,cAAc,gBAAgB;AAAA,MAC9B,KAAK;AAAA,IACP;AACA,UAAM,0BAA0B,cAAc;AAAA,MAC5C,KAAK;AAAA,IACP;AAEA,WAAO,qBAAqB,UAAU;AAAA,MACpC,OAAO,KAAK;AAAA,MACZ,QAAQ,mBAAmB,KAAK,KAAK;AAAA,MACrC,YAAY;AAAA,MACZ,gBAAgB,KAAK;AAAA,MACrB;AAAA,MACA,gBAAgB,mCAAS;AAAA,MACzB,cAAc,cAAc;AAAA,MAC5B,cAAc,KAAK;AAAA,IAAA,CACpB;AAAA,EAAA;AAAA,EAGH,mBAAgE;AAC1D,QAAA;AAGJ,QACE,KAAK,2BACL,KAAK,2BACL,KAAK,kBACL;AAEM,YAAA,IAAI,KAAK,YAAY,IAAI,KAAK,uBAAuB,IAAI,KAAK,gBAAgB,KAAK,KAAK,QAAQ;AAAA,IAAA,OACjG;AAEL,YAAM,UAAU,KAAK,WAAW,KAAK,oBAAoB;AACzD,YAAM,IAAI,KAAK,YAAY,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAA,IAAA;AAG1D,QAAI,KAAK,cAAc,oBAAoB,KAAK,iBAAiB;AAExD,aAAA,IAAI,KAAK,gBAAgB;AAAA,QAC9B,KAAK;AAAA,MAAA,CACN;AAAA,IACQ,WAAA,KAAK,cAAc,oBAAoB,KAAK,gBAAgB;AAE9D,aAAA,IAAI,KAAK,cAAc;AAAA,IAAA,OACzB;AAEC,YAAA,cAAc,KAAK,iBAAiB;AACnC,aAAA;AAAA,IAAA;AAGF,WAAA;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,iBAAyB;AACnB,QAAA;AAGJ,QACE,KAAK,2BACL,KAAK,2BACL,KAAK,kBACL;AACO,aAAA,IAAI,KAAK,uBAAuB,IAAI,KAAK,gBAAgB,KAAK,KAAK,QAAQ;AAAA,IAAA,OAC7E;AAEL,YAAM,UAAU,KAAK,WAAW,KAAK,oBAAoB;AACzD,aAAO,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAA,IAAA;AAGtC,QAAI,KAAK,cAAc,oBAAoB,KAAK,iBAAiB;AAC/D,aAAO,GAAG,IAAI,IAAI,KAAK,gBAAgB;AAAA,QACrC,KAAK;AAAA,MAAA,CACN;AAAA,IACQ,WAAA,KAAK,cAAc,oBAAoB,KAAK,gBAAgB;AAErE,aAAO,GAAG,IAAI,IAAI,KAAK,cAAc;AAAA,IAAA;AAGjC,UAAA,cAAc,KAAK,iBAAiB;AACnC,WAAA,GAAG,IAAI,GAAG,WAAW;AAAA,EAAA;AAAA,EAG9B,UAAU,SAAiB,SAAmC;AACtD,UAAA,SAAS,KAAK,iBAAiB;AAC9B,WAAA,mBAAmB,SAAS,QAAQ,OAAO;AAAA,EAAA;AAAA,EAGpD,MAAM,gBACJ,UACA,SAWA;AAEI,QAAA,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,KAAK,QAAQ,aAAa,KAAK,KAAK,IAAI;AAC1D,YAAM,QAAQ,MAAM;AAAA,QAClB;AAAA,QACA,SAAS,OAAO,IAAI,KAAK,YAAY,IAAI,SAAS;AAAA,MACpD;AACO,aAAA,EAAE,MAAM,QAAW,MAAM;AAAA,IAAA;AAI5B,UAAA,cAAc,MAAM,cAAc,QAAQ;AAG5C,QAAA,KAAK,cAAc,kBAAkB;AAGvC,YAAM,gBAAgB;AACtB,aAAO,EAAE,MAAM,cAAc,OAAc,OAAO,OAAU;AAAA,IAAA;AAI9D,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA,KAAK;AAAA,IACP;AACA,UAAM,gBAAgB,IAAI;AAAA,MACxB,cAAc,gBAAgB;AAAA,MAC9B,KAAK;AAAA,IACP;AACA,UAAM,0BAA0B,cAAc;AAAA,MAC5C,KAAK;AAAA,IACP;AAEA,WAAO,qBAAqB,aAAa;AAAA,MACvC,OAAO,KAAK;AAAA,MACZ,QAAQ,mBAAmB,KAAK,KAAK;AAAA,MACrC,YAAY;AAAA,MACZ,gBAAgB,KAAK;AAAA,MACrB;AAAA,MACA,gBAAgB,mCAAS;AAAA,MACzB,cAAc,cAAc;AAAA,MAC5B,cAAc,KAAK;AAAA,IAAA,CACpB;AAAA,EAAA;AAEL;"}
|
|
1
|
+
{"version":3,"file":"record-builder.js","sources":["../../../src/client/record-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n ODataFieldResponse,\n ExecuteOptions,\n ConditionallyWithODataAnnotations,\n ConditionallyWithSpecialColumns,\n NormalizeIncludeSpecialColumns,\n ExecuteMethodOptions,\n} from \"../types\";\nimport type {\n FMTable,\n InferSchemaOutputFromFMTable,\n ValidExpandTarget,\n ExtractTableName,\n ValidateNoContainerFields,\n} from \"../orm/table\";\nimport { getTableName, getNavigationPaths } from \"../orm/table\";\nimport { safeJsonParse } from \"./sanitize-json\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport { isColumn, type Column } from \"../orm/column\";\nimport {\n type ExpandConfig,\n type ExpandedRelations,\n ExpandBuilder,\n resolveTableId,\n mergeExecuteOptions,\n processODataResponse,\n getSchemaFromTable,\n processSelectWithRenames,\n buildSelectExpandQueryString,\n createODataRequest,\n} from \"./builders/index\";\nimport {\n type ResolveExpandedRelations,\n type ResolveExpandType,\n type SystemColumnsOption,\n type SystemColumnsFromOption,\n} from \"./query/types\";\nimport { createLogger, InternalLogger, Logger } from \"../logger\";\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 */\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 TSelect extends Record<string, Column<any, any, any, any>>,\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 Schema extends Record<string, any>,\n IsSingleField extends boolean,\n FieldColumn extends Column<any, any, any, any> | undefined,\n Selected extends\n | keyof Schema\n | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,\n Expands extends ExpandedRelations,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> = IsSingleField extends true\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 [Selected] extends [Record<string, Column<any, any, any, any>>]\n ? MapSelectToReturnType<Selected, Schema> &\n ResolveExpandedRelations<Expands> &\n SystemColumnsFromOption<SystemCols>\n : // Use tuple wrapping to prevent distribution over union of keys\n [Selected] extends [keyof Schema]\n ? Pick<Schema, Selected> &\n ResolveExpandedRelations<Expands> &\n SystemColumnsFromOption<SystemCols>\n : never;\n\nexport class RecordBuilder<\n Occ extends FMTable<any, any> = FMTable<any, any>,\n IsSingleField extends boolean = false,\n FieldColumn extends Column<any, any, any, any> | undefined = undefined,\n Selected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n | Record<\n string,\n Column<any, any, ExtractTableName<NonNullable<Occ>>>\n > = keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\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 table: Occ;\n private databaseName: string;\n private context: ExecutionContext;\n private recordId: string | number;\n private operation?: \"getSingleField\" | \"navigate\";\n private operationParam?: string;\n private operationColumn?: Column<any, any, any, any>;\n private isNavigateFromEntitySet?: boolean;\n private navigateRelation?: string;\n private navigateSourceTableName?: string;\n\n private databaseUseEntityIds: boolean;\n private databaseIncludeSpecialColumns: boolean;\n\n // Properties for select/expand support\n private selectedFields?: string[];\n private expandConfigs: ExpandConfig[] = [];\n // Mapping from field names to output keys (for renamed fields in select)\n private fieldMapping?: Record<string, string>;\n // System columns requested via select() second argument\n private systemColumns?: SystemColumnsOption;\n\n private 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 =\n 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(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit &\n FFetchOptions & {\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n } {\n const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);\n return {\n ...merged,\n includeSpecialColumns:\n 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(\n this.table,\n getTableName(this.table),\n this.context,\n useEntityIds,\n );\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 | Record<\n string,\n Column<any, any, ExtractTableName<NonNullable<Occ>>>\n > = Selected,\n NewSystemCols extends SystemColumnsOption | undefined = SystemCols,\n >(changes: {\n selectedFields?: string[];\n fieldMapping?: Record<string, string>;\n systemColumns?: NewSystemCols;\n }): RecordBuilder<\n Occ,\n false,\n FieldColumn,\n NewSelected,\n Expands,\n DatabaseIncludeSpecialColumns,\n NewSystemCols\n > {\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 newBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;\n newBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;\n newBuilder.systemColumns =\n changes.systemColumns !== undefined\n ? changes.systemColumns\n : this.systemColumns;\n newBuilder.expandConfigs = [...this.expandConfigs];\n // Preserve navigation context\n newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n newBuilder.navigateRelation = this.navigateRelation;\n newBuilder.navigateSourceTableName = this.navigateSourceTableName;\n newBuilder.operationColumn = this.operationColumn;\n return newBuilder;\n }\n\n getSingleField<\n TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>,\n >(\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(\n `Column ${column.toString()} is not from table ${tableName}`,\n );\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 newBuilder.operation = \"getSingleField\";\n newBuilder.operationColumn = column;\n newBuilder.operationParam = column.getFieldIdentifier(\n this.databaseUseEntityIds,\n );\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 /**\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 TSelect extends Record<\n string,\n Column<any, any, ExtractTableName<Occ>, false>\n >,\n TSystemCols extends SystemColumnsOption = {},\n >(\n fields: TSelect,\n systemColumns?: TSystemCols,\n ): RecordBuilder<\n Occ,\n false,\n FieldColumn,\n TSelect,\n Expands,\n DatabaseIncludeSpecialColumns,\n TSystemCols\n > {\n const tableName = getTableName(this.table);\n const { selectedFields, fieldMapping } = processSelectWithRenames(\n fields,\n tableName,\n this.logger,\n );\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:\n Object.keys(fieldMapping).length > 0 ? fieldMapping : undefined,\n systemColumns: systemColumns as any,\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 TargetTable extends FMTable<any, any>,\n TSelected extends\n | keyof InferSchemaOutputFromFMTable<TargetTable>\n | Record<\n string,\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<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {}\n >,\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 const newBuilder = new RecordBuilder<\n Occ,\n false,\n FieldColumn,\n Selected,\n any,\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\n // Copy existing state\n newBuilder.selectedFields = this.selectedFields;\n newBuilder.fieldMapping = this.fieldMapping;\n newBuilder.systemColumns = this.systemColumns;\n newBuilder.expandConfigs = [...this.expandConfigs];\n newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n newBuilder.navigateRelation = this.navigateRelation;\n newBuilder.navigateSourceTableName = this.navigateSourceTableName;\n newBuilder.operationColumn = this.operationColumn;\n\n // Use ExpandBuilder.processExpand to handle the expand logic\n const expandBuilder = new ExpandBuilder(\n this.databaseUseEntityIds,\n this.logger,\n );\n type TargetBuilder = QueryBuilder<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {}\n >;\n const expandConfig = expandBuilder.processExpand<\n TargetTable,\n TargetBuilder\n >(\n targetTable,\n this.table ?? undefined,\n callback as ((builder: TargetBuilder) => TargetBuilder) | undefined,\n () =>\n new QueryBuilder<\n TargetTable,\n any,\n any,\n any,\n any,\n DatabaseIncludeSpecialColumns,\n undefined\n >({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n }),\n );\n\n newBuilder.expandConfigs.push(expandConfig);\n return newBuilder as any;\n }\n\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 const builder = new QueryBuilder<\n TargetTable,\n any,\n any,\n any,\n any,\n DatabaseIncludeSpecialColumns,\n undefined\n >({\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 (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\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(\n this.table,\n getTableName(this.table),\n this.context,\n this.databaseUseEntityIds,\n );\n }\n\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): string {\n // Use merged includeSpecialColumns if provided, otherwise use database-level default\n const finalIncludeSpecialColumns =\n includeSpecialColumns ?? this.databaseIncludeSpecialColumns;\n\n return buildSelectExpandQueryString({\n selectedFields: this.selectedFields,\n expandConfigs: this.expandConfigs,\n table: this.table,\n useEntityIds: this.databaseUseEntityIds,\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<\n EO[\"includeSpecialColumns\"],\n DatabaseIncludeSpecialColumns\n >,\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 : Selected extends Record<string, Column<any, any, any>>\n ? true\n : Selected extends keyof InferSchemaOutputFromFMTable<\n NonNullable<Occ>\n >\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 (\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(\n options?.useEntityIds ?? this.databaseUseEntityIds,\n );\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(\n mergedOptions.includeSpecialColumns,\n );\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 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 // The type is extracted from the Column stored in FieldColumn generic\n const fieldResponse = response as ODataFieldResponse<any>;\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const expandBuilder = new ExpandBuilder(\n mergedOptions.useEntityIds ?? false,\n this.logger,\n );\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(\n this.expandConfigs,\n );\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 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.operationColumn) {\n // Use the column's getFieldIdentifier to support entity IDs\n url += `/${this.operationColumn.getFieldIdentifier(\n this.databaseUseEntityIds,\n )}`;\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(): string {\n let path: string;\n\n // Build the path depending on navigation context\n if (\n this.isNavigateFromEntitySet &&\n this.navigateSourceTableName &&\n this.navigateRelation\n ) {\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(this.databaseUseEntityIds);\n path = `/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n return `${path}/${this.operationColumn.getFieldIdentifier(\n this.databaseUseEntityIds,\n )}`;\n } else 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();\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(\n response,\n response.url || `/${this.databaseName}/${tableName}`,\n );\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 const fieldResponse = rawResponse as ODataFieldResponse<any>;\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(\n mergedOptions.useEntityIds ?? false,\n this.logger,\n );\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(\n this.expandConfigs,\n );\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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"}
|
|
@@ -12,6 +12,7 @@ export declare class UpdateBuilder<Occ extends FMTable<any, any>, ReturnPreferen
|
|
|
12
12
|
private data;
|
|
13
13
|
private returnPreference;
|
|
14
14
|
private databaseUseEntityIds;
|
|
15
|
+
private databaseIncludeSpecialColumns;
|
|
15
16
|
constructor(config: {
|
|
16
17
|
occurrence: Occ;
|
|
17
18
|
databaseName: string;
|
|
@@ -19,6 +20,7 @@ export declare class UpdateBuilder<Occ extends FMTable<any, any>, ReturnPreferen
|
|
|
19
20
|
data: Partial<InferSchemaOutputFromFMTable<Occ>>;
|
|
20
21
|
returnPreference: ReturnPreference;
|
|
21
22
|
databaseUseEntityIds?: boolean;
|
|
23
|
+
databaseIncludeSpecialColumns?: boolean;
|
|
22
24
|
});
|
|
23
25
|
/**
|
|
24
26
|
* Update a single record by ID
|
|
@@ -15,12 +15,14 @@ class UpdateBuilder {
|
|
|
15
15
|
__publicField(this, "data");
|
|
16
16
|
__publicField(this, "returnPreference");
|
|
17
17
|
__publicField(this, "databaseUseEntityIds");
|
|
18
|
+
__publicField(this, "databaseIncludeSpecialColumns");
|
|
18
19
|
this.table = config.occurrence;
|
|
19
20
|
this.databaseName = config.databaseName;
|
|
20
21
|
this.context = config.context;
|
|
21
22
|
this.data = config.data;
|
|
22
23
|
this.returnPreference = config.returnPreference;
|
|
23
24
|
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
25
|
+
this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* Update a single record by ID
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n ExecuteOptions,\n ExecuteMethodOptions,\n} from \"../types\";\nimport { getAcceptHeader } from \"../types\";\nimport type { FMTable, InferSchemaOutputFromFMTable } from \"../orm/table\";\nimport {\n getTableName,\n getTableId as getTableIdHelper,\n getBaseTableConfig,\n isUsingEntityIds,\n} from \"../orm/table\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport { transformFieldNamesToIds } from \"../transform\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport { validateAndTransformInput } from \"../validation\";\n\n/**\n * Initial update builder returned from EntitySet.update(data)\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class UpdateBuilder<\n Occ extends FMTable<any, any>,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> {\n private databaseName: string;\n private context: ExecutionContext;\n private table: Occ;\n private data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private returnPreference: ReturnPreference;\n\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Update a single record by ID\n * Returns updated count by default, or full record if returnFullRecord was set to true\n */\n byId(\n id: string | number,\n ): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns updated count by default, or full record if returnFullRecord was set to true\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(\n fn: (q: QueryBuilder<Occ>) => QueryBuilder<Occ>,\n ): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<Occ>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Let the user configure it\n const configuredBuilder = fn(queryBuilder);\n\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Can return either updated count or full record based on returnFullRecord option\n */\nexport class ExecutableUpdateBuilder<\n Occ extends FMTable<any, any>,\n IsByFilter extends boolean,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> implements\n ExecutableBuilder<\n ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>\n >\n{\n private databaseName: string;\n private context: ExecutionContext;\n private table: Occ;\n private data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<Occ>;\n private returnPreference: ReturnPreference;\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<Occ>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Helper to merge database-level useEntityIds with per-request options\n */\n private mergeExecuteOptions(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n // If useEntityIds is not set in options, use the database-level setting\n return {\n ...options,\n useEntityIds: options?.useEntityIds ?? this.databaseUseEntityIds,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n const contextDefault = this.context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIds ?? contextDefault;\n\n if (shouldUseIds) {\n if (!isUsingEntityIds(this.table)) {\n throw new Error(\n `useEntityIds is true but table \"${getTableName(this.table)}\" does not have entity IDs configured`,\n );\n }\n return getTableIdHelper(this.table);\n }\n\n return getTableName(this.table);\n }\n\n async execute(\n options?: ExecuteMethodOptions<ExecuteOptions>,\n ): Promise<\n Result<\n ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>\n >\n > {\n // Merge database-level useEntityIds with per-request options\n const mergedOptions = this.mergeExecuteOptions(options);\n\n // Get table identifier with override support\n const tableId = this.getTableId(mergedOptions.useEntityIds);\n\n // Validate and transform input data using input validators (writeValidators)\n let validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n\n try {\n validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n // If validation fails, return error immediately\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n } as any;\n }\n }\n\n // Transform field names to FMFIDs if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = mergedOptions.useEntityIds ?? false;\n\n const transformedData =\n this.table && shouldUseIds\n ? transformFieldNamesToIds(validatedData, this.table)\n : validatedData;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Update by filter: PATCH /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\n // The query string will have the tableId already transformed by QueryBuilder\n // Remove the leading \"/\" and table name from the query string as we'll build our own URL\n const tableName = getTableName(this.table);\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${tableName}`)\n ? queryString.slice(`/${tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Set Prefer header based on returnPreference\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.returnPreference === \"representation\") {\n headers[\"Prefer\"] = \"return=representation\";\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers,\n body: JSON.stringify(transformedData),\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: response as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n } else {\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof response === \"number\") {\n updatedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (response as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n // For batch operations, use database-level setting (no per-request override available here)\n // Note: Input validation happens in execute() and processResponse() for batch operations\n const tableId = this.getTableId(this.databaseUseEntityIds);\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData =\n this.table && this.databaseUseEntityIds\n ? transformFieldNamesToIds(this.data, this.table)\n : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n const queryString = this.queryBuilder.getQueryString();\n const tableName = getTableName(this.table);\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${tableName}`)\n ? queryString.slice(`/${tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\n body: JSON.stringify(transformedData),\n };\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): 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: getAcceptHeader(options?.includeODataAnnotations),\n },\n body: config.body,\n });\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<\n ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>\n >\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = getTableName(this.table);\n const error = await parseErrorResponse(\n response,\n response.url || `/${this.databaseName}/${tableName}`,\n );\n return { data: undefined, error };\n }\n\n // Check for empty response (204 No Content)\n const text = await response.text();\n if (!text || text.trim() === \"\") {\n // For 204 No Content, check the fmodata.affected_rows header\n const affectedRows = response.headers.get(\"fmodata.affected_rows\");\n const updatedCount = affectedRows ? parseInt(affectedRows, 10) : 1;\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n\n const rawResponse = JSON.parse(text);\n\n // Validate and transform input data using input validators (writeValidators)\n // This is needed for processResponse because it's called from batch operations\n // where the data hasn't been validated yet\n let validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n try {\n validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n } as any;\n }\n }\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: rawResponse as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n } else {\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof rawResponse === \"number\") {\n updatedCount = rawResponse;\n } else if (rawResponse && typeof rawResponse === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (rawResponse as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n }\n}\n"],"names":["getTableIdHelper"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n ExecuteOptions,\n ExecuteMethodOptions,\n} from \"../types\";\nimport { getAcceptHeader } from \"../types\";\nimport type { FMTable, InferSchemaOutputFromFMTable } from \"../orm/table\";\nimport {\n getTableName,\n getTableId as getTableIdHelper,\n getBaseTableConfig,\n isUsingEntityIds,\n} from \"../orm/table\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport { transformFieldNamesToIds } from \"../transform\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport { validateAndTransformInput } from \"../validation\";\n\n/**\n * Initial update builder returned from EntitySet.update(data)\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class UpdateBuilder<\n Occ extends FMTable<any, any>,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> {\n private databaseName: string;\n private context: ExecutionContext;\n private table: Occ;\n private data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private returnPreference: ReturnPreference;\n\n private databaseUseEntityIds: boolean;\n private databaseIncludeSpecialColumns: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n databaseIncludeSpecialColumns?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n this.databaseIncludeSpecialColumns =\n config.databaseIncludeSpecialColumns ?? false;\n }\n\n /**\n * Update a single record by ID\n * Returns updated count by default, or full record if returnFullRecord was set to true\n */\n byId(\n id: string | number,\n ): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns updated count by default, or full record if returnFullRecord was set to true\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(\n fn: (q: QueryBuilder<Occ>) => QueryBuilder<Occ>,\n ): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<Occ>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Let the user configure it\n const configuredBuilder = fn(queryBuilder);\n\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Can return either updated count or full record based on returnFullRecord option\n */\nexport class ExecutableUpdateBuilder<\n Occ extends FMTable<any, any>,\n IsByFilter extends boolean,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> implements\n ExecutableBuilder<\n ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>\n >\n{\n private databaseName: string;\n private context: ExecutionContext;\n private table: Occ;\n private data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<Occ>;\n private returnPreference: ReturnPreference;\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<Occ>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Helper to merge database-level useEntityIds with per-request options\n */\n private mergeExecuteOptions(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n // If useEntityIds is not set in options, use the database-level setting\n return {\n ...options,\n useEntityIds: options?.useEntityIds ?? this.databaseUseEntityIds,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n const contextDefault = this.context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIds ?? contextDefault;\n\n if (shouldUseIds) {\n if (!isUsingEntityIds(this.table)) {\n throw new Error(\n `useEntityIds is true but table \"${getTableName(this.table)}\" does not have entity IDs configured`,\n );\n }\n return getTableIdHelper(this.table);\n }\n\n return getTableName(this.table);\n }\n\n async execute(\n options?: ExecuteMethodOptions<ExecuteOptions>,\n ): Promise<\n Result<\n ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>\n >\n > {\n // Merge database-level useEntityIds with per-request options\n const mergedOptions = this.mergeExecuteOptions(options);\n\n // Get table identifier with override support\n const tableId = this.getTableId(mergedOptions.useEntityIds);\n\n // Validate and transform input data using input validators (writeValidators)\n let validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n\n try {\n validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n // If validation fails, return error immediately\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n } as any;\n }\n }\n\n // Transform field names to FMFIDs if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = mergedOptions.useEntityIds ?? false;\n\n const transformedData =\n this.table && shouldUseIds\n ? transformFieldNamesToIds(validatedData, this.table)\n : validatedData;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Update by filter: PATCH /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\n // The query string will have the tableId already transformed by QueryBuilder\n // Remove the leading \"/\" and table name from the query string as we'll build our own URL\n const tableName = getTableName(this.table);\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${tableName}`)\n ? queryString.slice(`/${tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Set Prefer header based on returnPreference\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.returnPreference === \"representation\") {\n headers[\"Prefer\"] = \"return=representation\";\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers,\n body: JSON.stringify(transformedData),\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: response as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n } else {\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof response === \"number\") {\n updatedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (response as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n // For batch operations, use database-level setting (no per-request override available here)\n // Note: Input validation happens in execute() and processResponse() for batch operations\n const tableId = this.getTableId(this.databaseUseEntityIds);\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData =\n this.table && this.databaseUseEntityIds\n ? transformFieldNamesToIds(this.data, this.table)\n : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n const queryString = this.queryBuilder.getQueryString();\n const tableName = getTableName(this.table);\n const queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${tableName}`)\n ? queryString.slice(`/${tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\n body: JSON.stringify(transformedData),\n };\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): 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: getAcceptHeader(options?.includeODataAnnotations),\n },\n body: config.body,\n });\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<\n ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>\n >\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = getTableName(this.table);\n const error = await parseErrorResponse(\n response,\n response.url || `/${this.databaseName}/${tableName}`,\n );\n return { data: undefined, error };\n }\n\n // Check for empty response (204 No Content)\n const text = await response.text();\n if (!text || text.trim() === \"\") {\n // For 204 No Content, check the fmodata.affected_rows header\n const affectedRows = response.headers.get(\"fmodata.affected_rows\");\n const updatedCount = affectedRows ? parseInt(affectedRows, 10) : 1;\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n\n const rawResponse = JSON.parse(text);\n\n // Validate and transform input data using input validators (writeValidators)\n // This is needed for processResponse because it's called from batch operations\n // where the data hasn't been validated yet\n let validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n try {\n validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n } as any;\n }\n }\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: rawResponse as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n } else {\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof rawResponse === \"number\") {\n updatedCount = rawResponse;\n } else if (rawResponse && typeof rawResponse === \"object\") {\n // Check if the response has a count property (fallback)\n updatedCount = (rawResponse as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n }\n}\n"],"names":["getTableIdHelper"],"mappings":"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"}
|