@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.
Files changed (74) hide show
  1. package/README.md +198 -0
  2. package/dist/esm/client/builders/default-select.d.ts +4 -1
  3. package/dist/esm/client/builders/default-select.js +3 -2
  4. package/dist/esm/client/builders/default-select.js.map +1 -1
  5. package/dist/esm/client/builders/expand-builder.js.map +1 -1
  6. package/dist/esm/client/builders/query-string-builder.d.ts +1 -0
  7. package/dist/esm/client/builders/query-string-builder.js.map +1 -1
  8. package/dist/esm/client/builders/response-processor.d.ts +2 -0
  9. package/dist/esm/client/builders/response-processor.js +8 -3
  10. package/dist/esm/client/builders/response-processor.js.map +1 -1
  11. package/dist/esm/client/database.d.ts +27 -4
  12. package/dist/esm/client/database.js +17 -4
  13. package/dist/esm/client/database.js.map +1 -1
  14. package/dist/esm/client/delete-builder.d.ts +2 -0
  15. package/dist/esm/client/delete-builder.js +2 -0
  16. package/dist/esm/client/delete-builder.js.map +1 -1
  17. package/dist/esm/client/entity-set.d.ts +8 -7
  18. package/dist/esm/client/entity-set.js +21 -26
  19. package/dist/esm/client/entity-set.js.map +1 -1
  20. package/dist/esm/client/error-parser.js.map +1 -1
  21. package/dist/esm/client/filemaker-odata.d.ts +15 -2
  22. package/dist/esm/client/filemaker-odata.js +25 -2
  23. package/dist/esm/client/filemaker-odata.js.map +1 -1
  24. package/dist/esm/client/insert-builder.d.ts +2 -0
  25. package/dist/esm/client/insert-builder.js +2 -0
  26. package/dist/esm/client/insert-builder.js.map +1 -1
  27. package/dist/esm/client/query/query-builder.d.ts +26 -15
  28. package/dist/esm/client/query/query-builder.js +43 -9
  29. package/dist/esm/client/query/query-builder.js.map +1 -1
  30. package/dist/esm/client/query/response-processor.d.ts +1 -0
  31. package/dist/esm/client/query/types.d.ts +24 -3
  32. package/dist/esm/client/record-builder.d.ts +24 -13
  33. package/dist/esm/client/record-builder.js +50 -17
  34. package/dist/esm/client/record-builder.js.map +1 -1
  35. package/dist/esm/client/update-builder.d.ts +2 -0
  36. package/dist/esm/client/update-builder.js +2 -0
  37. package/dist/esm/client/update-builder.js.map +1 -1
  38. package/dist/esm/client/webhook-builder.d.ts +126 -0
  39. package/dist/esm/client/webhook-builder.js +197 -0
  40. package/dist/esm/client/webhook-builder.js.map +1 -0
  41. package/dist/esm/index.d.ts +1 -0
  42. package/dist/esm/orm/field-builders.d.ts +12 -2
  43. package/dist/esm/orm/field-builders.js +18 -2
  44. package/dist/esm/orm/field-builders.js.map +1 -1
  45. package/dist/esm/orm/table.d.ts +23 -10
  46. package/dist/esm/orm/table.js +17 -30
  47. package/dist/esm/orm/table.js.map +1 -1
  48. package/dist/esm/types.d.ts +32 -2
  49. package/dist/esm/types.js.map +1 -1
  50. package/dist/esm/validation.d.ts +5 -5
  51. package/dist/esm/validation.js +44 -13
  52. package/dist/esm/validation.js.map +1 -1
  53. package/package.json +2 -2
  54. package/src/client/builders/default-select.ts +12 -1
  55. package/src/client/builders/expand-builder.ts +1 -1
  56. package/src/client/builders/query-string-builder.ts +6 -0
  57. package/src/client/builders/response-processor.ts +10 -0
  58. package/src/client/database.ts +54 -12
  59. package/src/client/delete-builder.ts +5 -1
  60. package/src/client/entity-set.ts +72 -44
  61. package/src/client/error-parser.ts +3 -0
  62. package/src/client/filemaker-odata.ts +39 -6
  63. package/src/client/insert-builder.ts +4 -0
  64. package/src/client/query/query-builder.ts +198 -35
  65. package/src/client/query/response-processor.ts +15 -25
  66. package/src/client/query/types.ts +35 -6
  67. package/src/client/record-builder.ts +159 -32
  68. package/src/client/update-builder.ts +4 -1
  69. package/src/client/webhook-builder.ts +285 -0
  70. package/src/index.ts +6 -0
  71. package/src/orm/field-builders.ts +24 -2
  72. package/src/orm/table.ts +40 -48
  73. package/src/types.ts +62 -6
  74. 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>>>(fields: TSelect): RecordBuilder<Occ, false, FieldColumn, TSelect, Expands>;
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
- return mergeExecuteOptions(options, this.databaseUseEntityIds);
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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"}