@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
|
@@ -24,6 +24,7 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
24
24
|
private serverUrl: string;
|
|
25
25
|
private auth: Auth;
|
|
26
26
|
private useEntityIds: boolean = false;
|
|
27
|
+
private includeSpecialColumns: boolean = false;
|
|
27
28
|
private logger: InternalLogger;
|
|
28
29
|
constructor(config: {
|
|
29
30
|
serverUrl: string;
|
|
@@ -63,6 +64,22 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
63
64
|
return this.useEntityIds;
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/**
|
|
68
|
+
* @internal
|
|
69
|
+
* Sets whether to include special columns (ROWID and ROWMODID) in requests
|
|
70
|
+
*/
|
|
71
|
+
_setIncludeSpecialColumns(includeSpecialColumns: boolean): void {
|
|
72
|
+
this.includeSpecialColumns = includeSpecialColumns;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
* Gets whether to include special columns (ROWID and ROWMODID) in requests
|
|
78
|
+
*/
|
|
79
|
+
_getIncludeSpecialColumns(): boolean {
|
|
80
|
+
return this.includeSpecialColumns;
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
/**
|
|
67
84
|
* @internal
|
|
68
85
|
* Gets the base URL for OData requests
|
|
@@ -84,7 +101,11 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
84
101
|
*/
|
|
85
102
|
async _makeRequest<T>(
|
|
86
103
|
url: string,
|
|
87
|
-
options?: RequestInit &
|
|
104
|
+
options?: RequestInit &
|
|
105
|
+
FFetchOptions & {
|
|
106
|
+
useEntityIds?: boolean;
|
|
107
|
+
includeSpecialColumns?: boolean;
|
|
108
|
+
},
|
|
88
109
|
): Promise<Result<T>> {
|
|
89
110
|
const logger = this._getLogger();
|
|
90
111
|
const baseUrl = `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
|
|
@@ -92,10 +113,21 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
92
113
|
|
|
93
114
|
// Use per-request override if provided, otherwise use the database-level setting
|
|
94
115
|
const useEntityIds = options?.useEntityIds ?? this.useEntityIds;
|
|
116
|
+
const includeSpecialColumns =
|
|
117
|
+
options?.includeSpecialColumns ?? this.includeSpecialColumns;
|
|
95
118
|
|
|
96
119
|
// Get includeODataAnnotations from options (it's passed through from execute options)
|
|
97
120
|
const includeODataAnnotations = (options as any)?.includeODataAnnotations;
|
|
98
121
|
|
|
122
|
+
// Build Prefer header as comma-separated list when multiple preferences are set
|
|
123
|
+
const preferValues: string[] = [];
|
|
124
|
+
if (useEntityIds) {
|
|
125
|
+
preferValues.push("fmodata.entity-ids");
|
|
126
|
+
}
|
|
127
|
+
if (includeSpecialColumns) {
|
|
128
|
+
preferValues.push("fmodata.include-specialcolumns");
|
|
129
|
+
}
|
|
130
|
+
|
|
99
131
|
const headers = {
|
|
100
132
|
Authorization:
|
|
101
133
|
"apiKey" in this.auth
|
|
@@ -103,7 +135,7 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
103
135
|
: `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,
|
|
104
136
|
"Content-Type": "application/json",
|
|
105
137
|
Accept: getAcceptHeader(includeODataAnnotations),
|
|
106
|
-
...(
|
|
138
|
+
...(preferValues.length > 0 ? { Prefer: preferValues.join(", ") } : {}),
|
|
107
139
|
...(options?.headers || {}),
|
|
108
140
|
};
|
|
109
141
|
|
|
@@ -271,13 +303,14 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
271
303
|
}
|
|
272
304
|
}
|
|
273
305
|
|
|
274
|
-
database(
|
|
306
|
+
database<IncludeSpecialColumns extends boolean = false>(
|
|
275
307
|
name: string,
|
|
276
308
|
config?: {
|
|
277
309
|
useEntityIds?: boolean;
|
|
310
|
+
includeSpecialColumns?: IncludeSpecialColumns;
|
|
278
311
|
},
|
|
279
|
-
): Database {
|
|
280
|
-
return new Database(name, this, config);
|
|
312
|
+
): Database<IncludeSpecialColumns> {
|
|
313
|
+
return new Database<IncludeSpecialColumns>(name, this, config);
|
|
281
314
|
}
|
|
282
315
|
|
|
283
316
|
/**
|
|
@@ -287,7 +320,7 @@ export class FMServerConnection implements ExecutionContext {
|
|
|
287
320
|
async listDatabaseNames(): Promise<string[]> {
|
|
288
321
|
const result = await this._makeRequest<{
|
|
289
322
|
value?: Array<{ name: string }>;
|
|
290
|
-
}>("/");
|
|
323
|
+
}>("/$metadata", { headers: { Accept: "application/json" } });
|
|
291
324
|
if (result.error) {
|
|
292
325
|
throw result.error;
|
|
293
326
|
}
|
|
@@ -52,6 +52,7 @@ export class InsertBuilder<
|
|
|
52
52
|
private returnPreference: ReturnPreference;
|
|
53
53
|
|
|
54
54
|
private databaseUseEntityIds: boolean;
|
|
55
|
+
private databaseIncludeSpecialColumns: boolean;
|
|
55
56
|
|
|
56
57
|
constructor(config: {
|
|
57
58
|
occurrence?: Occ;
|
|
@@ -60,6 +61,7 @@ export class InsertBuilder<
|
|
|
60
61
|
data: Partial<InferSchemaOutputFromFMTable<NonNullable<Occ>>>;
|
|
61
62
|
returnPreference?: ReturnPreference;
|
|
62
63
|
databaseUseEntityIds?: boolean;
|
|
64
|
+
databaseIncludeSpecialColumns?: boolean;
|
|
63
65
|
}) {
|
|
64
66
|
this.table = config.occurrence;
|
|
65
67
|
this.databaseName = config.databaseName;
|
|
@@ -68,6 +70,8 @@ export class InsertBuilder<
|
|
|
68
70
|
this.returnPreference = (config.returnPreference ||
|
|
69
71
|
"representation") as ReturnPreference;
|
|
70
72
|
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
73
|
+
this.databaseIncludeSpecialColumns =
|
|
74
|
+
config.databaseIncludeSpecialColumns ?? false;
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
/**
|
|
@@ -6,15 +6,13 @@ import type {
|
|
|
6
6
|
Result,
|
|
7
7
|
ExecuteOptions,
|
|
8
8
|
ConditionallyWithODataAnnotations,
|
|
9
|
-
|
|
9
|
+
ConditionallyWithSpecialColumns,
|
|
10
|
+
NormalizeIncludeSpecialColumns,
|
|
10
11
|
ExecuteMethodOptions,
|
|
11
12
|
} from "../../types";
|
|
12
13
|
import { RecordCountMismatchError } from "../../errors";
|
|
13
14
|
import { type FFetchOptions } from "@fetchkit/ffetch";
|
|
14
|
-
import {
|
|
15
|
-
transformFieldNamesArray,
|
|
16
|
-
transformOrderByField,
|
|
17
|
-
} from "../../transform";
|
|
15
|
+
import { transformOrderByField } from "../../transform";
|
|
18
16
|
import { safeJsonParse } from "../sanitize-json";
|
|
19
17
|
import { parseErrorResponse } from "../error-parser";
|
|
20
18
|
import { isColumn, type Column } from "../../orm/column";
|
|
@@ -28,7 +26,6 @@ import {
|
|
|
28
26
|
type InferSchemaOutputFromFMTable,
|
|
29
27
|
type ValidExpandTarget,
|
|
30
28
|
type ExtractTableName,
|
|
31
|
-
type ValidateNoContainerFields,
|
|
32
29
|
getTableName,
|
|
33
30
|
} from "../../orm/table";
|
|
34
31
|
import {
|
|
@@ -37,14 +34,17 @@ import {
|
|
|
37
34
|
type ExpandedRelations,
|
|
38
35
|
resolveTableId,
|
|
39
36
|
mergeExecuteOptions,
|
|
40
|
-
formatSelectFields,
|
|
41
37
|
processQueryResponse,
|
|
42
38
|
processSelectWithRenames,
|
|
43
39
|
buildSelectExpandQueryString,
|
|
44
40
|
createODataRequest,
|
|
45
41
|
} from "../builders/index";
|
|
46
42
|
import { QueryUrlBuilder, type NavigationConfig } from "./url-builder";
|
|
47
|
-
import type {
|
|
43
|
+
import type {
|
|
44
|
+
TypeSafeOrderBy,
|
|
45
|
+
QueryReturnType,
|
|
46
|
+
SystemColumnsOption,
|
|
47
|
+
} from "./types";
|
|
48
48
|
import { createLogger, InternalLogger } from "../../logger";
|
|
49
49
|
|
|
50
50
|
// Re-export QueryReturnType for backward compatibility
|
|
@@ -70,6 +70,8 @@ export class QueryBuilder<
|
|
|
70
70
|
SingleMode extends "exact" | "maybe" | false = false,
|
|
71
71
|
IsCount extends boolean = false,
|
|
72
72
|
Expands extends ExpandedRelations = {},
|
|
73
|
+
DatabaseIncludeSpecialColumns extends boolean = false,
|
|
74
|
+
SystemCols extends SystemColumnsOption | undefined = undefined,
|
|
73
75
|
> implements
|
|
74
76
|
ExecutableBuilder<
|
|
75
77
|
QueryReturnType<
|
|
@@ -77,7 +79,8 @@ export class QueryBuilder<
|
|
|
77
79
|
Selected,
|
|
78
80
|
SingleMode,
|
|
79
81
|
IsCount,
|
|
80
|
-
Expands
|
|
82
|
+
Expands,
|
|
83
|
+
SystemCols
|
|
81
84
|
>
|
|
82
85
|
>
|
|
83
86
|
{
|
|
@@ -92,10 +95,13 @@ export class QueryBuilder<
|
|
|
92
95
|
private context: ExecutionContext;
|
|
93
96
|
private navigation?: NavigationConfig;
|
|
94
97
|
private databaseUseEntityIds: boolean;
|
|
98
|
+
private databaseIncludeSpecialColumns: boolean;
|
|
95
99
|
private expandBuilder: ExpandBuilder;
|
|
96
100
|
private urlBuilder: QueryUrlBuilder;
|
|
97
101
|
// Mapping from field names to output keys (for renamed fields in select)
|
|
98
102
|
private fieldMapping?: Record<string, string>;
|
|
103
|
+
// System columns requested via select() second argument
|
|
104
|
+
private systemColumns?: SystemColumnsOption;
|
|
99
105
|
private logger: InternalLogger;
|
|
100
106
|
|
|
101
107
|
constructor(config: {
|
|
@@ -103,12 +109,15 @@ export class QueryBuilder<
|
|
|
103
109
|
databaseName: string;
|
|
104
110
|
context: ExecutionContext;
|
|
105
111
|
databaseUseEntityIds?: boolean;
|
|
112
|
+
databaseIncludeSpecialColumns?: boolean;
|
|
106
113
|
}) {
|
|
107
114
|
this.occurrence = config.occurrence;
|
|
108
115
|
this.databaseName = config.databaseName;
|
|
109
116
|
this.context = config.context;
|
|
110
117
|
this.logger = config.context?._getLogger?.() ?? createLogger();
|
|
111
118
|
this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
|
|
119
|
+
this.databaseIncludeSpecialColumns =
|
|
120
|
+
config.databaseIncludeSpecialColumns ?? false;
|
|
112
121
|
this.expandBuilder = new ExpandBuilder(
|
|
113
122
|
this.databaseUseEntityIds,
|
|
114
123
|
this.logger,
|
|
@@ -121,12 +130,21 @@ export class QueryBuilder<
|
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
/**
|
|
124
|
-
* Helper to merge database-level useEntityIds with per-request options
|
|
133
|
+
* Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options
|
|
125
134
|
*/
|
|
126
135
|
private mergeExecuteOptions(
|
|
127
136
|
options?: RequestInit & FFetchOptions & ExecuteOptions,
|
|
128
|
-
): RequestInit &
|
|
129
|
-
|
|
137
|
+
): RequestInit &
|
|
138
|
+
FFetchOptions & {
|
|
139
|
+
useEntityIds?: boolean;
|
|
140
|
+
includeSpecialColumns?: boolean;
|
|
141
|
+
} {
|
|
142
|
+
const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);
|
|
143
|
+
return {
|
|
144
|
+
...merged,
|
|
145
|
+
includeSpecialColumns:
|
|
146
|
+
options?.includeSpecialColumns ?? this.databaseIncludeSpecialColumns,
|
|
147
|
+
};
|
|
130
148
|
}
|
|
131
149
|
|
|
132
150
|
/**
|
|
@@ -159,24 +177,37 @@ export class QueryBuilder<
|
|
|
159
177
|
| Record<string, Column<any, any, ExtractTableName<Occ>>> = Selected,
|
|
160
178
|
NewSingle extends "exact" | "maybe" | false = SingleMode,
|
|
161
179
|
NewCount extends boolean = IsCount,
|
|
180
|
+
NewSystemCols extends SystemColumnsOption | undefined = SystemCols,
|
|
162
181
|
>(changes: {
|
|
163
182
|
selectedFields?: NewSelected;
|
|
164
183
|
singleMode?: NewSingle;
|
|
165
184
|
isCountMode?: NewCount;
|
|
166
185
|
queryOptions?: Partial<QueryOptions<InferSchemaOutputFromFMTable<Occ>>>;
|
|
167
186
|
fieldMapping?: Record<string, string>;
|
|
168
|
-
|
|
187
|
+
systemColumns?: NewSystemCols;
|
|
188
|
+
}): QueryBuilder<
|
|
189
|
+
Occ,
|
|
190
|
+
NewSelected,
|
|
191
|
+
NewSingle,
|
|
192
|
+
NewCount,
|
|
193
|
+
Expands,
|
|
194
|
+
DatabaseIncludeSpecialColumns,
|
|
195
|
+
NewSystemCols
|
|
196
|
+
> {
|
|
169
197
|
const newBuilder = new QueryBuilder<
|
|
170
198
|
Occ,
|
|
171
199
|
NewSelected,
|
|
172
200
|
NewSingle,
|
|
173
201
|
NewCount,
|
|
174
|
-
Expands
|
|
202
|
+
Expands,
|
|
203
|
+
DatabaseIncludeSpecialColumns,
|
|
204
|
+
NewSystemCols
|
|
175
205
|
>({
|
|
176
206
|
occurrence: this.occurrence,
|
|
177
207
|
databaseName: this.databaseName,
|
|
178
208
|
context: this.context,
|
|
179
209
|
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
210
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,
|
|
180
211
|
});
|
|
181
212
|
newBuilder.queryOptions = {
|
|
182
213
|
...this.queryOptions,
|
|
@@ -186,6 +217,10 @@ export class QueryBuilder<
|
|
|
186
217
|
newBuilder.singleMode = (changes.singleMode ?? this.singleMode) as any;
|
|
187
218
|
newBuilder.isCountMode = (changes.isCountMode ?? this.isCountMode) as any;
|
|
188
219
|
newBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;
|
|
220
|
+
newBuilder.systemColumns =
|
|
221
|
+
changes.systemColumns !== undefined
|
|
222
|
+
? changes.systemColumns
|
|
223
|
+
: this.systemColumns;
|
|
189
224
|
// Copy navigation metadata
|
|
190
225
|
newBuilder.navigation = this.navigation;
|
|
191
226
|
newBuilder.urlBuilder = new QueryUrlBuilder(
|
|
@@ -207,7 +242,15 @@ export class QueryBuilder<
|
|
|
207
242
|
* userEmail: users.email // renamed!
|
|
208
243
|
* })
|
|
209
244
|
*
|
|
245
|
+
* @example
|
|
246
|
+
* // Include system columns (ROWID, ROWMODID) when using select()
|
|
247
|
+
* db.from(users).list().select(
|
|
248
|
+
* { name: users.name },
|
|
249
|
+
* { ROWID: true, ROWMODID: true }
|
|
250
|
+
* )
|
|
251
|
+
*
|
|
210
252
|
* @param fields - Object mapping output keys to column references (container fields excluded)
|
|
253
|
+
* @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)
|
|
211
254
|
* @returns QueryBuilder with updated selected fields
|
|
212
255
|
*/
|
|
213
256
|
select<
|
|
@@ -215,7 +258,19 @@ export class QueryBuilder<
|
|
|
215
258
|
string,
|
|
216
259
|
Column<any, any, ExtractTableName<Occ>, false>
|
|
217
260
|
>,
|
|
218
|
-
|
|
261
|
+
TSystemCols extends SystemColumnsOption = {},
|
|
262
|
+
>(
|
|
263
|
+
fields: TSelect,
|
|
264
|
+
systemColumns?: TSystemCols,
|
|
265
|
+
): QueryBuilder<
|
|
266
|
+
Occ,
|
|
267
|
+
TSelect,
|
|
268
|
+
SingleMode,
|
|
269
|
+
IsCount,
|
|
270
|
+
Expands,
|
|
271
|
+
DatabaseIncludeSpecialColumns,
|
|
272
|
+
TSystemCols
|
|
273
|
+
> {
|
|
219
274
|
const tableName = getTableName(this.occurrence);
|
|
220
275
|
const { selectedFields, fieldMapping } = processSelectWithRenames(
|
|
221
276
|
fields,
|
|
@@ -223,13 +278,23 @@ export class QueryBuilder<
|
|
|
223
278
|
this.logger,
|
|
224
279
|
);
|
|
225
280
|
|
|
281
|
+
// Add system columns to selectedFields if requested
|
|
282
|
+
const finalSelectedFields = [...selectedFields];
|
|
283
|
+
if (systemColumns?.ROWID) {
|
|
284
|
+
finalSelectedFields.push("ROWID");
|
|
285
|
+
}
|
|
286
|
+
if (systemColumns?.ROWMODID) {
|
|
287
|
+
finalSelectedFields.push("ROWMODID");
|
|
288
|
+
}
|
|
289
|
+
|
|
226
290
|
return this.cloneWithChanges({
|
|
227
291
|
selectedFields: fields as any,
|
|
228
292
|
queryOptions: {
|
|
229
|
-
select:
|
|
293
|
+
select: finalSelectedFields,
|
|
230
294
|
},
|
|
231
295
|
fieldMapping:
|
|
232
296
|
Object.keys(fieldMapping).length > 0 ? fieldMapping : undefined,
|
|
297
|
+
systemColumns: systemColumns as any,
|
|
233
298
|
});
|
|
234
299
|
}
|
|
235
300
|
|
|
@@ -245,7 +310,15 @@ export class QueryBuilder<
|
|
|
245
310
|
*/
|
|
246
311
|
where(
|
|
247
312
|
expression: FilterExpression | string,
|
|
248
|
-
): QueryBuilder<
|
|
313
|
+
): QueryBuilder<
|
|
314
|
+
Occ,
|
|
315
|
+
Selected,
|
|
316
|
+
SingleMode,
|
|
317
|
+
IsCount,
|
|
318
|
+
Expands,
|
|
319
|
+
DatabaseIncludeSpecialColumns,
|
|
320
|
+
SystemCols
|
|
321
|
+
> {
|
|
249
322
|
// Handle raw string filters (escape hatch)
|
|
250
323
|
if (typeof expression === "string") {
|
|
251
324
|
this.queryOptions.filter = expression;
|
|
@@ -295,7 +368,15 @@ export class QueryBuilder<
|
|
|
295
368
|
| OrderByExpression<ExtractTableName<Occ>>
|
|
296
369
|
>,
|
|
297
370
|
]
|
|
298
|
-
): QueryBuilder<
|
|
371
|
+
): QueryBuilder<
|
|
372
|
+
Occ,
|
|
373
|
+
Selected,
|
|
374
|
+
SingleMode,
|
|
375
|
+
IsCount,
|
|
376
|
+
Expands,
|
|
377
|
+
DatabaseIncludeSpecialColumns,
|
|
378
|
+
SystemCols
|
|
379
|
+
> {
|
|
299
380
|
const tableName = getTableName(this.occurrence);
|
|
300
381
|
|
|
301
382
|
// Handle variadic arguments (multiple fields)
|
|
@@ -440,14 +521,30 @@ export class QueryBuilder<
|
|
|
440
521
|
|
|
441
522
|
top(
|
|
442
523
|
count: number,
|
|
443
|
-
): QueryBuilder<
|
|
524
|
+
): QueryBuilder<
|
|
525
|
+
Occ,
|
|
526
|
+
Selected,
|
|
527
|
+
SingleMode,
|
|
528
|
+
IsCount,
|
|
529
|
+
Expands,
|
|
530
|
+
DatabaseIncludeSpecialColumns,
|
|
531
|
+
SystemCols
|
|
532
|
+
> {
|
|
444
533
|
this.queryOptions.top = count;
|
|
445
534
|
return this;
|
|
446
535
|
}
|
|
447
536
|
|
|
448
537
|
skip(
|
|
449
538
|
count: number,
|
|
450
|
-
): QueryBuilder<
|
|
539
|
+
): QueryBuilder<
|
|
540
|
+
Occ,
|
|
541
|
+
Selected,
|
|
542
|
+
SingleMode,
|
|
543
|
+
IsCount,
|
|
544
|
+
Expands,
|
|
545
|
+
DatabaseIncludeSpecialColumns,
|
|
546
|
+
SystemCols
|
|
547
|
+
> {
|
|
451
548
|
this.queryOptions.skip = count;
|
|
452
549
|
return this;
|
|
453
550
|
}
|
|
@@ -483,7 +580,9 @@ export class QueryBuilder<
|
|
|
483
580
|
selected: TSelected;
|
|
484
581
|
nested: TNestedExpands;
|
|
485
582
|
};
|
|
486
|
-
}
|
|
583
|
+
},
|
|
584
|
+
DatabaseIncludeSpecialColumns,
|
|
585
|
+
SystemCols
|
|
487
586
|
> {
|
|
488
587
|
// Use ExpandBuilder.processExpand to handle the expand logic
|
|
489
588
|
type TargetBuilder = QueryBuilder<
|
|
@@ -491,7 +590,8 @@ export class QueryBuilder<
|
|
|
491
590
|
keyof InferSchemaOutputFromFMTable<TargetTable>,
|
|
492
591
|
false,
|
|
493
592
|
false,
|
|
494
|
-
{}
|
|
593
|
+
{},
|
|
594
|
+
DatabaseIncludeSpecialColumns
|
|
495
595
|
>;
|
|
496
596
|
const expandConfig = this.expandBuilder.processExpand<
|
|
497
597
|
TargetTable,
|
|
@@ -501,11 +601,20 @@ export class QueryBuilder<
|
|
|
501
601
|
this.occurrence,
|
|
502
602
|
callback as ((builder: TargetBuilder) => TargetBuilder) | undefined,
|
|
503
603
|
() =>
|
|
504
|
-
new QueryBuilder<
|
|
604
|
+
new QueryBuilder<
|
|
605
|
+
TargetTable,
|
|
606
|
+
any,
|
|
607
|
+
any,
|
|
608
|
+
any,
|
|
609
|
+
any,
|
|
610
|
+
DatabaseIncludeSpecialColumns,
|
|
611
|
+
undefined
|
|
612
|
+
>({
|
|
505
613
|
occurrence: targetTable,
|
|
506
614
|
databaseName: this.databaseName,
|
|
507
615
|
context: this.context,
|
|
508
616
|
databaseUseEntityIds: this.databaseUseEntityIds,
|
|
617
|
+
databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,
|
|
509
618
|
}),
|
|
510
619
|
);
|
|
511
620
|
|
|
@@ -513,15 +622,39 @@ export class QueryBuilder<
|
|
|
513
622
|
return this as any;
|
|
514
623
|
}
|
|
515
624
|
|
|
516
|
-
single(): QueryBuilder<
|
|
625
|
+
single(): QueryBuilder<
|
|
626
|
+
Occ,
|
|
627
|
+
Selected,
|
|
628
|
+
"exact",
|
|
629
|
+
IsCount,
|
|
630
|
+
Expands,
|
|
631
|
+
DatabaseIncludeSpecialColumns,
|
|
632
|
+
SystemCols
|
|
633
|
+
> {
|
|
517
634
|
return this.cloneWithChanges({ singleMode: "exact" as const });
|
|
518
635
|
}
|
|
519
636
|
|
|
520
|
-
maybeSingle(): QueryBuilder<
|
|
637
|
+
maybeSingle(): QueryBuilder<
|
|
638
|
+
Occ,
|
|
639
|
+
Selected,
|
|
640
|
+
"maybe",
|
|
641
|
+
IsCount,
|
|
642
|
+
Expands,
|
|
643
|
+
DatabaseIncludeSpecialColumns,
|
|
644
|
+
SystemCols
|
|
645
|
+
> {
|
|
521
646
|
return this.cloneWithChanges({ singleMode: "maybe" as const });
|
|
522
647
|
}
|
|
523
648
|
|
|
524
|
-
count(): QueryBuilder<
|
|
649
|
+
count(): QueryBuilder<
|
|
650
|
+
Occ,
|
|
651
|
+
Selected,
|
|
652
|
+
SingleMode,
|
|
653
|
+
true,
|
|
654
|
+
Expands,
|
|
655
|
+
DatabaseIncludeSpecialColumns,
|
|
656
|
+
SystemCols
|
|
657
|
+
> {
|
|
525
658
|
return this.cloneWithChanges({
|
|
526
659
|
isCountMode: true as const,
|
|
527
660
|
queryOptions: { count: true },
|
|
@@ -531,7 +664,7 @@ export class QueryBuilder<
|
|
|
531
664
|
/**
|
|
532
665
|
* Builds the OData query string from current query options and expand configs.
|
|
533
666
|
*/
|
|
534
|
-
private buildQueryString(): string {
|
|
667
|
+
private buildQueryString(includeSpecialColumns?: boolean): string {
|
|
535
668
|
// Build query without expand and select (we'll add them manually if using entity IDs)
|
|
536
669
|
const queryOptionsWithoutExpandAndSelect = { ...this.queryOptions };
|
|
537
670
|
const originalSelect = queryOptionsWithoutExpandAndSelect.select;
|
|
@@ -547,12 +680,17 @@ export class QueryBuilder<
|
|
|
547
680
|
: [String(originalSelect)]
|
|
548
681
|
: undefined;
|
|
549
682
|
|
|
683
|
+
// Use merged includeSpecialColumns if provided, otherwise use database-level default
|
|
684
|
+
const finalIncludeSpecialColumns =
|
|
685
|
+
includeSpecialColumns ?? this.databaseIncludeSpecialColumns;
|
|
686
|
+
|
|
550
687
|
const selectExpandString = buildSelectExpandQueryString({
|
|
551
688
|
selectedFields: selectArray,
|
|
552
689
|
expandConfigs: this.expandConfigs,
|
|
553
690
|
table: this.occurrence,
|
|
554
691
|
useEntityIds: this.databaseUseEntityIds,
|
|
555
692
|
logger: this.logger,
|
|
693
|
+
includeSpecialColumns: finalIncludeSpecialColumns,
|
|
556
694
|
});
|
|
557
695
|
|
|
558
696
|
// Append select/expand to existing query string
|
|
@@ -573,19 +711,35 @@ export class QueryBuilder<
|
|
|
573
711
|
): Promise<
|
|
574
712
|
Result<
|
|
575
713
|
ConditionallyWithODataAnnotations<
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
714
|
+
ConditionallyWithSpecialColumns<
|
|
715
|
+
QueryReturnType<
|
|
716
|
+
InferSchemaOutputFromFMTable<Occ>,
|
|
717
|
+
Selected,
|
|
718
|
+
SingleMode,
|
|
719
|
+
IsCount,
|
|
720
|
+
Expands,
|
|
721
|
+
SystemCols
|
|
722
|
+
>,
|
|
723
|
+
// Use the merged value: if explicitly provided in options, use that; otherwise use database default
|
|
724
|
+
NormalizeIncludeSpecialColumns<
|
|
725
|
+
EO["includeSpecialColumns"],
|
|
726
|
+
DatabaseIncludeSpecialColumns
|
|
727
|
+
>,
|
|
728
|
+
// Check if select was applied: if Selected is Record (object select) or a subset of keys, select was applied
|
|
729
|
+
Selected extends Record<string, Column<any, any, any>>
|
|
730
|
+
? true
|
|
731
|
+
: Selected extends keyof InferSchemaOutputFromFMTable<Occ>
|
|
732
|
+
? false
|
|
733
|
+
: true
|
|
582
734
|
>,
|
|
583
735
|
EO["includeODataAnnotations"] extends true ? true : false
|
|
584
736
|
>
|
|
585
737
|
>
|
|
586
738
|
> {
|
|
587
739
|
const mergedOptions = this.mergeExecuteOptions(options);
|
|
588
|
-
const queryString = this.buildQueryString(
|
|
740
|
+
const queryString = this.buildQueryString(
|
|
741
|
+
mergedOptions.includeSpecialColumns,
|
|
742
|
+
);
|
|
589
743
|
|
|
590
744
|
// Handle $count endpoint
|
|
591
745
|
if (this.isCountMode) {
|
|
@@ -618,6 +772,9 @@ export class QueryBuilder<
|
|
|
618
772
|
return { data: undefined, error: result.error };
|
|
619
773
|
}
|
|
620
774
|
|
|
775
|
+
// Check if select was applied (runtime check)
|
|
776
|
+
const hasSelect = this.queryOptions.select !== undefined;
|
|
777
|
+
|
|
621
778
|
return processQueryResponse(result.data, {
|
|
622
779
|
occurrence: this.occurrence,
|
|
623
780
|
singleMode: this.singleMode,
|
|
@@ -625,6 +782,7 @@ export class QueryBuilder<
|
|
|
625
782
|
expandConfigs: this.expandConfigs,
|
|
626
783
|
skipValidation: options?.skipValidation,
|
|
627
784
|
useEntityIds: mergedOptions.useEntityIds,
|
|
785
|
+
includeSpecialColumns: mergedOptions.includeSpecialColumns,
|
|
628
786
|
fieldMapping: this.fieldMapping,
|
|
629
787
|
logger: this.logger,
|
|
630
788
|
});
|
|
@@ -667,7 +825,8 @@ export class QueryBuilder<
|
|
|
667
825
|
Selected,
|
|
668
826
|
SingleMode,
|
|
669
827
|
IsCount,
|
|
670
|
-
Expands
|
|
828
|
+
Expands,
|
|
829
|
+
SystemCols
|
|
671
830
|
>
|
|
672
831
|
>
|
|
673
832
|
> {
|
|
@@ -728,6 +887,9 @@ export class QueryBuilder<
|
|
|
728
887
|
}
|
|
729
888
|
|
|
730
889
|
const mergedOptions = this.mergeExecuteOptions(options);
|
|
890
|
+
// Check if select was applied (runtime check)
|
|
891
|
+
const hasSelect = this.queryOptions.select !== undefined;
|
|
892
|
+
|
|
731
893
|
return processQueryResponse(rawData, {
|
|
732
894
|
occurrence: this.occurrence,
|
|
733
895
|
singleMode: this.singleMode,
|
|
@@ -735,6 +897,7 @@ export class QueryBuilder<
|
|
|
735
897
|
expandConfigs: this.expandConfigs,
|
|
736
898
|
skipValidation: options?.skipValidation,
|
|
737
899
|
useEntityIds: mergedOptions.useEntityIds,
|
|
900
|
+
includeSpecialColumns: mergedOptions.includeSpecialColumns,
|
|
738
901
|
fieldMapping: this.fieldMapping,
|
|
739
902
|
logger: this.logger,
|
|
740
903
|
});
|