@proofkit/fmodata 0.1.0-alpha.14 → 0.1.0-alpha.16

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 (61) hide show
  1. package/README.md +4 -165
  2. package/dist/esm/client/batch-builder.d.ts +2 -3
  3. package/dist/esm/client/batch-builder.js.map +1 -1
  4. package/dist/esm/client/builders/expand-builder.d.ts +1 -1
  5. package/dist/esm/client/builders/expand-builder.js.map +1 -1
  6. package/dist/esm/client/builders/select-mixin.d.ts +2 -2
  7. package/dist/esm/client/builders/select-mixin.js.map +1 -1
  8. package/dist/esm/client/delete-builder.d.ts +2 -5
  9. package/dist/esm/client/delete-builder.js.map +1 -1
  10. package/dist/esm/client/entity-set.d.ts +3 -13
  11. package/dist/esm/client/entity-set.js.map +1 -1
  12. package/dist/esm/client/error-parser.js.map +1 -1
  13. package/dist/esm/client/insert-builder.d.ts +2 -3
  14. package/dist/esm/client/insert-builder.js.map +1 -1
  15. package/dist/esm/client/query/query-builder.d.ts +11 -19
  16. package/dist/esm/client/query/query-builder.js +7 -76
  17. package/dist/esm/client/query/query-builder.js.map +1 -1
  18. package/dist/esm/client/query/types.d.ts +5 -5
  19. package/dist/esm/client/query-builder.d.ts +1 -1
  20. package/dist/esm/client/record-builder.d.ts +8 -9
  21. package/dist/esm/client/record-builder.js.map +1 -1
  22. package/dist/esm/client/update-builder.d.ts +2 -5
  23. package/dist/esm/client/update-builder.js.map +1 -1
  24. package/dist/esm/index.d.ts +2 -2
  25. package/dist/esm/orm/column.d.ts +21 -4
  26. package/dist/esm/orm/column.js +5 -2
  27. package/dist/esm/orm/column.js.map +1 -1
  28. package/dist/esm/orm/field-builders.d.ts +1 -1
  29. package/dist/esm/orm/field-builders.js +1 -1
  30. package/dist/esm/orm/field-builders.js.map +1 -1
  31. package/dist/esm/orm/operators.d.ts +19 -19
  32. package/dist/esm/orm/operators.js +31 -12
  33. package/dist/esm/orm/operators.js.map +1 -1
  34. package/dist/esm/orm/table.d.ts +10 -9
  35. package/dist/esm/orm/table.js +5 -3
  36. package/dist/esm/orm/table.js.map +1 -1
  37. package/dist/esm/transform.js +1 -5
  38. package/dist/esm/transform.js.map +1 -1
  39. package/dist/esm/types.d.ts +26 -2
  40. package/dist/esm/types.js.map +1 -1
  41. package/package.json +3 -1
  42. package/src/client/batch-builder.ts +2 -1
  43. package/src/client/builders/expand-builder.ts +3 -3
  44. package/src/client/builders/select-mixin.ts +2 -3
  45. package/src/client/delete-builder.ts +2 -1
  46. package/src/client/entity-set.ts +26 -8
  47. package/src/client/error-parser.ts +3 -0
  48. package/src/client/insert-builder.ts +2 -1
  49. package/src/client/query/query-builder.ts +27 -126
  50. package/src/client/query/types.ts +6 -5
  51. package/src/client/query-builder.ts +1 -1
  52. package/src/client/record-builder.ts +22 -9
  53. package/src/client/update-builder.ts +17 -8
  54. package/src/index.ts +6 -15
  55. package/src/orm/column.ts +33 -5
  56. package/src/orm/field-builders.ts +1 -1
  57. package/src/orm/operators.ts +105 -51
  58. package/src/orm/table.ts +21 -13
  59. package/src/types.ts +33 -6
  60. package/dist/esm/filter-types.d.ts +0 -76
  61. package/src/filter-types.ts +0 -97
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofkit/fmodata",
3
- "version": "0.1.0-alpha.14",
3
+ "version": "0.1.0-alpha.16",
4
4
  "description": "FileMaker OData API client",
5
5
  "repository": "git@github.com:proofgeist/proofkit.git",
6
6
  "author": "Eric <37158449+eluce2@users.noreply.github.com>",
@@ -28,6 +28,8 @@
28
28
  "tsc": "tsc --noEmit",
29
29
  "test:typecheck": "vitest run --typecheck",
30
30
  "test:watch": "vitest --typecheck",
31
+ "test:build": "pnpm build && TEST_BUILD=true vitest run --typecheck",
32
+ "test:watch:build": "TEST_BUILD=true vitest --typecheck",
31
33
  "test:e2e": "op inject -i op.env -o .env.local -f && vitest run tests/e2e.test.ts",
32
34
  "capture": "op inject -i op.env -o .env.local -f && tsx scripts/capture-responses.ts",
33
35
  "knip": "knip",
@@ -5,6 +5,7 @@ import type {
5
5
  ExecuteOptions,
6
6
  BatchResult,
7
7
  BatchItemResult,
8
+ ExecuteMethodOptions,
8
9
  } from "../types";
9
10
  import { BatchTruncatedError } from "../errors";
10
11
  import { type FFetchOptions } from "@fetchkit/ffetch";
@@ -148,7 +149,7 @@ export class BatchBuilder<Builders extends readonly ExecutableBuilder<any>[]> {
148
149
  * @returns A BatchResult containing individual results for each operation
149
150
  */
150
151
  async execute<EO extends ExecuteOptions>(
151
- options?: RequestInit & FFetchOptions & EO,
152
+ options?: ExecuteMethodOptions<EO>,
152
153
  ): Promise<BatchResult<ExtractTupleTypes<Builders>>> {
153
154
  const baseUrl = this.context._getBaseUrl?.();
154
155
  if (!baseUrl) {
@@ -77,11 +77,11 @@ export class ExpandBuilder {
77
77
  * @param builderFactory - Function that creates a QueryBuilder for the target table
78
78
  * @returns ExpandConfig to add to the builder's expandConfigs array
79
79
  */
80
- processExpand<TargetTable extends FMTable<any, any>>(
80
+ processExpand<TargetTable extends FMTable<any, any>, Builder = any>(
81
81
  targetTable: TargetTable,
82
82
  sourceTable: FMTable<any, any> | undefined,
83
- callback?: (builder: any) => any,
84
- builderFactory?: () => any,
83
+ callback?: (builder: Builder) => Builder,
84
+ builderFactory?: () => Builder,
85
85
  ): ExpandConfig {
86
86
  // Extract name and validate
87
87
  const relationName = getTableName(targetTable);
@@ -8,7 +8,7 @@ import { isColumn, type Column } from "../../orm/column";
8
8
  * @returns Object with selectedFields array
9
9
  */
10
10
  export function processSelectFields(
11
- ...fields: (string | Column<any, string>)[]
11
+ ...fields: (string | Column<any, any, string>)[]
12
12
  ): { selectedFields: string[] } {
13
13
  const fieldNames = fields.map((field) => {
14
14
  if (isColumn(field)) {
@@ -29,7 +29,7 @@ export function processSelectFields(
29
29
  * @returns Object with selectedFields array and fieldMapping for renamed fields
30
30
  */
31
31
  export function processSelectWithRenames<TTableName extends string>(
32
- fields: Record<string, Column<any, TTableName>>,
32
+ fields: Record<string, Column<any, any, TTableName>>,
33
33
  tableName: string,
34
34
  ): { selectedFields: string[]; fieldMapping: Record<string, string> } {
35
35
  const selectedFields: string[] = [];
@@ -71,4 +71,3 @@ export function processSelectWithRenames<TTableName extends string>(
71
71
  export class SelectMixin {
72
72
  static processSelect = processSelectFields;
73
73
  }
74
-
@@ -4,6 +4,7 @@ import type {
4
4
  Result,
5
5
  WithSystemFields,
6
6
  ExecuteOptions,
7
+ ExecuteMethodOptions,
7
8
  } from "../types";
8
9
  import { getAcceptHeader } from "../types";
9
10
  import type { FMTable, InferSchemaOutputFromFMTable } from "../orm/table";
@@ -147,7 +148,7 @@ export class ExecutableDeleteBuilder<Occ extends FMTable<any, any>>
147
148
  }
148
149
 
149
150
  async execute(
150
- options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },
151
+ options?: ExecuteMethodOptions<ExecuteOptions>,
151
152
  ): Promise<Result<{ deletedCount: number }>> {
152
153
  // Merge database-level useEntityIds with per-request options
153
154
  const mergedOptions = this.mergeExecuteOptions(options);
@@ -1,6 +1,6 @@
1
1
  import type { ExecutionContext, InferSchemaType } from "../types";
2
2
  import type { StandardSchemaV1 } from "@standard-schema/spec";
3
- import { QueryBuilder } from "./query";
3
+ import { QueryBuilder } from "./query/index";
4
4
  import { RecordBuilder } from "./record-builder";
5
5
  import { InsertBuilder } from "./insert-builder";
6
6
  import { DeleteBuilder } from "./delete-builder";
@@ -16,6 +16,7 @@ import type {
16
16
  ExtractTableName,
17
17
  FMTableWithColumns,
18
18
  InferFieldOutput,
19
+ ColumnMap,
19
20
  } from "../orm/table";
20
21
  import {
21
22
  FMTable as FMTableClass,
@@ -42,7 +43,7 @@ type ExtractDefaultSelect<O> =
42
43
  type ExtractColumnsFromOcc<T> =
43
44
  T extends FMTable<infer TFields, infer TName, any>
44
45
  ? TFields extends Record<string, FieldBuilder<any, any, any, any>>
45
- ? { [K in keyof TFields]: Column<InferFieldOutput<TFields[K]>, TName> }
46
+ ? ColumnMap<TFields, TName>
46
47
  : never
47
48
  : never;
48
49
 
@@ -87,7 +88,13 @@ export class EntitySet<Occ extends FMTable<any, any>> {
87
88
  });
88
89
  }
89
90
 
90
- list() {
91
+ list(): QueryBuilder<
92
+ Occ,
93
+ keyof InferSchemaOutputFromFMTable<Occ>,
94
+ false,
95
+ false,
96
+ {}
97
+ > {
91
98
  const builder = new QueryBuilder<Occ>({
92
99
  occurrence: this.occurrence as Occ,
93
100
  databaseName: this.databaseName,
@@ -117,18 +124,29 @@ export class EntitySet<Occ extends FMTable<any, any>> {
117
124
  if (defaultSelectValue === "schema") {
118
125
  // Use getTableColumns to get all columns and select them
119
126
  // This is equivalent to select(getTableColumns(occurrence))
120
- // Use ExtractColumnsFromOcc to preserve the properly-typed column types
127
+ // Cast to the declared return type - runtime behavior handles the actual selection
121
128
  const allColumns = getTableColumns(
122
129
  this.occurrence as any,
123
130
  ) as ExtractColumnsFromOcc<Occ>;
124
- return builder.select(allColumns).top(1000);
131
+ return builder.select(allColumns).top(1000) as QueryBuilder<
132
+ Occ,
133
+ keyof InferSchemaOutputFromFMTable<Occ>,
134
+ false,
135
+ false,
136
+ {}
137
+ >;
125
138
  } else if (typeof defaultSelectValue === "object") {
126
139
  // defaultSelectValue is a select object (Record<string, Column>)
127
- // Use it directly with select()
128
- // Use ExtractColumnsFromOcc to preserve the properly-typed column types
140
+ // Cast to the declared return type - runtime behavior handles the actual selection
129
141
  return builder
130
142
  .select(defaultSelectValue as ExtractColumnsFromOcc<Occ>)
131
- .top(1000);
143
+ .top(1000) as QueryBuilder<
144
+ Occ,
145
+ keyof InferSchemaOutputFromFMTable<Occ>,
146
+ false,
147
+ false,
148
+ {}
149
+ >;
132
150
  }
133
151
  // If defaultSelect is "all", no changes needed (current behavior)
134
152
  }
@@ -57,3 +57,6 @@ export async function parseErrorResponse(
57
57
 
58
58
 
59
59
 
60
+
61
+
62
+
@@ -6,6 +6,7 @@ import type {
6
6
  InferSchemaType,
7
7
  ExecuteOptions,
8
8
  ConditionallyWithODataAnnotations,
9
+ ExecuteMethodOptions,
9
10
  } from "../types";
10
11
  import { getAcceptHeader } from "../types";
11
12
  import type { FMTable } from "../orm/table";
@@ -142,7 +143,7 @@ export class InsertBuilder<
142
143
  }
143
144
 
144
145
  async execute<EO extends ExecuteOptions>(
145
- options?: RequestInit & FFetchOptions & EO,
146
+ options?: ExecuteMethodOptions<EO>,
146
147
  ): Promise<
147
148
  Result<
148
149
  ReturnPreference extends "minimal"
@@ -7,12 +7,11 @@ import type {
7
7
  ExecuteOptions,
8
8
  ConditionallyWithODataAnnotations,
9
9
  ExtractSchemaFromOccurrence,
10
+ ExecuteMethodOptions,
10
11
  } from "../../types";
11
- import type { Filter } from "../../filter-types";
12
12
  import { RecordCountMismatchError } from "../../errors";
13
13
  import { type FFetchOptions } from "@fetchkit/ffetch";
14
14
  import {
15
- transformFieldName,
16
15
  transformFieldNamesArray,
17
16
  transformOrderByField,
18
17
  } from "../../transform";
@@ -43,7 +42,7 @@ import {
43
42
  processSelectWithRenames,
44
43
  buildSelectExpandQueryString,
45
44
  createODataRequest,
46
- } from "../builders";
45
+ } from "../builders/index";
47
46
  import { QueryUrlBuilder, type NavigationConfig } from "./url-builder";
48
47
  import type { TypeSafeOrderBy, QueryReturnType } from "./types";
49
48
 
@@ -65,7 +64,7 @@ export class QueryBuilder<
65
64
  | keyof InferSchemaOutputFromFMTable<Occ>
66
65
  | Record<
67
66
  string,
68
- Column<any, ExtractTableName<Occ>>
67
+ Column<any, any, ExtractTableName<Occ>>
69
68
  > = keyof InferSchemaOutputFromFMTable<Occ>,
70
69
  SingleMode extends "exact" | "maybe" | false = false,
71
70
  IsCount extends boolean = false,
@@ -151,7 +150,7 @@ export class QueryBuilder<
151
150
  private cloneWithChanges<
152
151
  NewSelected extends
153
152
  | keyof InferSchemaOutputFromFMTable<Occ>
154
- | Record<string, Column<any, ExtractTableName<Occ>>> = Selected,
153
+ | Record<string, Column<any, any, ExtractTableName<Occ>>> = Selected,
155
154
  NewSingle extends "exact" | "maybe" | false = SingleMode,
156
155
  NewCount extends boolean = IsCount,
157
156
  >(changes: {
@@ -206,7 +205,7 @@ export class QueryBuilder<
206
205
  * @returns QueryBuilder with updated selected fields
207
206
  */
208
207
  select<
209
- TSelect extends Record<string, Column<any, ExtractTableName<Occ>, false>>,
208
+ TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false>>,
210
209
  >(fields: TSelect): QueryBuilder<Occ, TSelect, SingleMode, IsCount, Expands> {
211
210
  const tableName = getTableName(this.occurrence);
212
211
  const { selectedFields, fieldMapping } = processSelectWithRenames(
@@ -224,131 +223,24 @@ export class QueryBuilder<
224
223
  });
225
224
  }
226
225
 
227
- /**
228
- * Transforms our filter format to odata-query's expected format
229
- * - Arrays of operators are converted to AND conditions
230
- * - Single operator objects pass through as-is
231
- * - Shorthand values are handled by odata-query
232
- */
233
- private transformFilter(
234
- filter: Filter<ExtractSchemaFromOccurrence<Occ>>,
235
- ): QueryOptions<InferSchemaOutputFromFMTable<Occ>>["filter"] {
236
- if (typeof filter === "string") {
237
- // Raw string filters pass through
238
- return filter;
239
- }
240
-
241
- if (Array.isArray(filter)) {
242
- // Array of filters - odata-query handles this as implicit AND
243
- return filter.map((f) => this.transformFilter(f as any)) as any;
244
- }
245
-
246
- // Check if it's a logical filter (and/or/not)
247
- if ("and" in filter || "or" in filter || "not" in filter) {
248
- const result: any = {};
249
- if ("and" in filter && Array.isArray(filter.and)) {
250
- result.and = filter.and.map((f: any) => this.transformFilter(f));
251
- }
252
- if ("or" in filter && Array.isArray(filter.or)) {
253
- result.or = filter.or.map((f: any) => this.transformFilter(f));
254
- }
255
- if ("not" in filter && filter.not) {
256
- result.not = this.transformFilter(filter.not as any);
257
- }
258
- return result;
259
- }
260
-
261
- // Transform field filters
262
- const result: any = {};
263
- const andConditions: any[] = [];
264
-
265
- for (const [field, value] of Object.entries(filter)) {
266
- // Transform field name to FMFID if using entity IDs AND the feature is enabled
267
- const shouldTransform = this.occurrence && this.databaseUseEntityIds;
268
- const fieldId = shouldTransform
269
- ? transformFieldName(field, this.occurrence!)
270
- : field;
271
-
272
- if (Array.isArray(value)) {
273
- // Array of operators - convert to AND conditions
274
- if (value.length === 1) {
275
- // Single operator in array - unwrap it
276
- result[fieldId] = value[0];
277
- } else {
278
- // Multiple operators - combine with AND
279
- // Create separate conditions for each operator
280
- for (const op of value) {
281
- andConditions.push({ [fieldId]: op });
282
- }
283
- }
284
- } else if (
285
- value &&
286
- typeof value === "object" &&
287
- !(value instanceof Date) &&
288
- !Array.isArray(value)
289
- ) {
290
- // Check if it's an operator object (has operator keys like eq, gt, etc.)
291
- const operatorKeys = [
292
- "eq",
293
- "ne",
294
- "gt",
295
- "ge",
296
- "lt",
297
- "le",
298
- "contains",
299
- "startswith",
300
- "endswith",
301
- "in",
302
- ];
303
- const isOperatorObject = operatorKeys.some((key) => key in value);
304
-
305
- if (isOperatorObject) {
306
- // Single operator object - pass through
307
- result[fieldId] = value;
308
- } else {
309
- // Regular object - might be nested filter, pass through
310
- result[fieldId] = value;
311
- }
312
- } else {
313
- // Primitive value (shorthand) - pass through
314
- result[fieldId] = value;
315
- }
316
- }
317
-
318
- // If we have AND conditions from arrays, combine them
319
- if (andConditions.length > 0) {
320
- if (Object.keys(result).length > 0) {
321
- // We have both regular fields and array-derived AND conditions
322
- // Combine everything with AND
323
- return { and: [...andConditions, result] };
324
- } else {
325
- // Only array-derived AND conditions
326
- return { and: andConditions };
327
- }
328
- }
329
-
330
- return result;
331
- }
332
-
333
- filter(
334
- filter: Filter<ExtractSchemaFromOccurrence<Occ>>,
335
- ): QueryBuilder<Occ, Selected, SingleMode, IsCount, Expands> {
336
- // Transform our filter format to odata-query's expected format
337
- this.queryOptions.filter = this.transformFilter(filter) as any;
338
- return this;
339
- }
340
-
341
226
  /**
342
227
  * Filter results using operator expressions (new ORM-style API).
343
228
  * Supports eq, gt, lt, and, or, etc. operators with Column references.
229
+ * Also supports raw OData filter strings as an escape hatch.
344
230
  *
345
231
  * @example
346
232
  * .where(eq(users.hobby, "reading"))
347
233
  * .where(and(eq(users.active, true), gt(users.age, 18)))
234
+ * .where("status eq 'active'") // Raw OData string escape hatch
348
235
  */
349
236
  where(
350
- expression: FilterExpression,
237
+ expression: FilterExpression | string,
351
238
  ): QueryBuilder<Occ, Selected, SingleMode, IsCount, Expands> {
239
+ // Handle raw string filters (escape hatch)
240
+ if (typeof expression === "string") {
241
+ this.queryOptions.filter = expression;
242
+ return this;
243
+ }
352
244
  // Convert FilterExpression to OData filter string
353
245
  const filterString = expression.toODataFilter(this.databaseUseEntityIds);
354
246
  this.queryOptions.filter = filterString;
@@ -383,13 +275,13 @@ export class QueryBuilder<
383
275
  ...orderByArgs:
384
276
  | [
385
277
  | TypeSafeOrderBy<InferSchemaOutputFromFMTable<Occ>>
386
- | Column<any, ExtractTableName<Occ>>
278
+ | Column<any, any, ExtractTableName<Occ>>
387
279
  | OrderByExpression<ExtractTableName<Occ>>,
388
280
  ]
389
281
  | [
390
- Column<any, ExtractTableName<Occ>>,
282
+ Column<any, any, ExtractTableName<Occ>>,
391
283
  ...Array<
392
- | Column<any, ExtractTableName<Occ>>
284
+ | Column<any, any, ExtractTableName<Occ>>
393
285
  | OrderByExpression<ExtractTableName<Occ>>
394
286
  >,
395
287
  ]
@@ -573,7 +465,16 @@ export class QueryBuilder<
573
465
  }
574
466
  > {
575
467
  // Use ExpandBuilder.processExpand to handle the expand logic
576
- const expandConfig = this.expandBuilder.processExpand(
468
+ type TargetBuilder = QueryBuilder<
469
+ TargetTable,
470
+ keyof InferSchemaOutputFromFMTable<TargetTable>,
471
+ false,
472
+ false
473
+ >;
474
+ const expandConfig = this.expandBuilder.processExpand<
475
+ TargetTable,
476
+ TargetBuilder
477
+ >(
577
478
  targetTable,
578
479
  this.occurrence,
579
480
  callback,
@@ -645,7 +546,7 @@ export class QueryBuilder<
645
546
  }
646
547
 
647
548
  async execute<EO extends ExecuteOptions>(
648
- options?: RequestInit & FFetchOptions & EO,
549
+ options?: ExecuteMethodOptions<EO>,
649
550
  ): Promise<
650
551
  Result<
651
552
  ConditionallyWithODataAnnotations<
@@ -28,16 +28,17 @@ export type ExpandedRelations = Record<string, { schema: any; selected: any }>;
28
28
 
29
29
  /**
30
30
  * Extract the value type from a Column.
31
- * This uses the phantom type stored in Column to get the actual value type.
31
+ * This uses the phantom type stored in Column to get the actual value type (output type for reading).
32
32
  */
33
- type ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;
33
+ type ExtractColumnType<C> =
34
+ C extends Column<infer T, any, any, any> ? T : never;
34
35
 
35
36
  /**
36
37
  * Map a select object to its return type.
37
38
  * For each key in the select object, extract the type from the corresponding Column.
38
39
  */
39
40
  type MapSelectToReturnType<
40
- TSelect extends Record<string, Column<any, any>>,
41
+ TSelect extends Record<string, Column<any, any, any, any>>,
41
42
  TSchema extends Record<string, any>,
42
43
  > = {
43
44
  [K in keyof TSelect]: ExtractColumnType<TSelect[K]>;
@@ -45,14 +46,14 @@ type MapSelectToReturnType<
45
46
 
46
47
  export type QueryReturnType<
47
48
  T extends Record<string, any>,
48
- Selected extends keyof T | Record<string, Column<any, any>>,
49
+ Selected extends keyof T | Record<string, Column<any, any, any, any>>,
49
50
  SingleMode extends "exact" | "maybe" | false,
50
51
  IsCount extends boolean,
51
52
  Expands extends ExpandedRelations,
52
53
  > = IsCount extends true
53
54
  ? number
54
55
  : // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions
55
- [Selected] extends [Record<string, Column<any, any>>]
56
+ [Selected] extends [Record<string, Column<any, any, any, any>>]
56
57
  ? SingleMode extends "exact"
57
58
  ? MapSelectToReturnType<Selected, T> & {
58
59
  [K in keyof Expands]: Pick<
@@ -5,4 +5,4 @@ export {
5
5
  type TypeSafeOrderBy,
6
6
  type ExpandedRelations,
7
7
  type QueryReturnType,
8
- } from "./query";
8
+ } from "./query/index";
@@ -5,6 +5,7 @@ import type {
5
5
  ODataFieldResponse,
6
6
  ExecuteOptions,
7
7
  ConditionallyWithODataAnnotations,
8
+ ExecuteMethodOptions,
8
9
  } from "../types";
9
10
  import type {
10
11
  FMTable,
@@ -30,7 +31,7 @@ import {
30
31
  processSelectWithRenames,
31
32
  buildSelectExpandQueryString,
32
33
  createODataRequest,
33
- } from "./builders";
34
+ } from "./builders/index";
34
35
 
35
36
  /**
36
37
  * Extract the value type from a Column.
@@ -43,7 +44,7 @@ type ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;
43
44
  * For each key in the select object, extract the type from the corresponding Column.
44
45
  */
45
46
  type MapSelectToReturnType<
46
- TSelect extends Record<string, Column<any, any>>,
47
+ TSelect extends Record<string, Column<any, any, any, any>>,
47
48
  TSchema extends Record<string, any>,
48
49
  > = {
49
50
  [K in keyof TSelect]: ExtractColumnType<TSelect[K]>;
@@ -56,12 +57,12 @@ export type RecordReturnType<
56
57
  FieldKey extends keyof Schema,
57
58
  Selected extends
58
59
  | keyof Schema
59
- | Record<string, Column<any, ExtractTableName<FMTable<any, any>>>>,
60
+ | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,
60
61
  Expands extends ExpandedRelations,
61
62
  > = IsSingleField extends true
62
63
  ? Schema[FieldKey]
63
64
  : // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions
64
- [Selected] extends [Record<string, Column<any, any>>]
65
+ [Selected] extends [Record<string, Column<any, any, any, any>>]
65
66
  ? MapSelectToReturnType<Selected, Schema> & {
66
67
  [K in keyof Expands]: Pick<
67
68
  Expands[K]["schema"],
@@ -88,7 +89,7 @@ export class RecordBuilder<
88
89
  | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>
89
90
  | Record<
90
91
  string,
91
- Column<any, ExtractTableName<NonNullable<Occ>>>
92
+ Column<any, any, ExtractTableName<NonNullable<Occ>>>
92
93
  > = keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,
93
94
  Expands extends ExpandedRelations = {},
94
95
  > implements
@@ -168,7 +169,7 @@ export class RecordBuilder<
168
169
  | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>
169
170
  | Record<
170
171
  string,
171
- Column<any, ExtractTableName<NonNullable<Occ>>>
172
+ Column<any, any, ExtractTableName<NonNullable<Occ>>>
172
173
  > = Selected,
173
174
  >(changes: {
174
175
  selectedFields?: string[];
@@ -245,7 +246,10 @@ export class RecordBuilder<
245
246
  * @returns RecordBuilder with updated selected fields
246
247
  */
247
248
  select<
248
- TSelect extends Record<string, Column<any, ExtractTableName<Occ>, false>>,
249
+ TSelect extends Record<
250
+ string,
251
+ Column<any, any, ExtractTableName<Occ>, false>
252
+ >,
249
253
  >(fields: TSelect): RecordBuilder<Occ, false, FieldKey, TSelect, Expands> {
250
254
  const tableName = getTableName(this.table);
251
255
  const { selectedFields, fieldMapping } = processSelectWithRenames(
@@ -316,7 +320,16 @@ export class RecordBuilder<
316
320
 
317
321
  // Use ExpandBuilder.processExpand to handle the expand logic
318
322
  const expandBuilder = new ExpandBuilder(this.databaseUseEntityIds);
319
- const expandConfig = expandBuilder.processExpand(
323
+ type TargetBuilder = QueryBuilder<
324
+ TargetTable,
325
+ keyof InferSchemaOutputFromFMTable<TargetTable>,
326
+ false,
327
+ false
328
+ >;
329
+ const expandConfig = expandBuilder.processExpand<
330
+ TargetTable,
331
+ TargetBuilder
332
+ >(
320
333
  targetTable,
321
334
  this.table ?? undefined,
322
335
  callback,
@@ -414,7 +427,7 @@ export class RecordBuilder<
414
427
  }
415
428
 
416
429
  async execute<EO extends ExecuteOptions>(
417
- options?: RequestInit & FFetchOptions & EO,
430
+ options?: ExecuteMethodOptions<EO>,
418
431
  ): Promise<
419
432
  Result<
420
433
  ConditionallyWithODataAnnotations<
@@ -4,6 +4,7 @@ import type {
4
4
  Result,
5
5
  WithSystemFields,
6
6
  ExecuteOptions,
7
+ ExecuteMethodOptions,
7
8
  } from "../types";
8
9
  import { getAcceptHeader } from "../types";
9
10
  import type { FMTable, InferSchemaOutputFromFMTable } from "../orm/table";
@@ -76,9 +77,7 @@ export class UpdateBuilder<
76
77
  * @param fn Callback that receives a QueryBuilder for building the filter
77
78
  */
78
79
  where(
79
- fn: (
80
- q: QueryBuilder<Occ>,
81
- ) => QueryBuilder<Occ>,
80
+ fn: (q: QueryBuilder<Occ>) => QueryBuilder<Occ>,
82
81
  ): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {
83
82
  // Create a QueryBuilder for the user to configure
84
83
  const queryBuilder = new QueryBuilder<Occ>({
@@ -114,7 +113,9 @@ export class ExecutableUpdateBuilder<
114
113
  ReturnPreference extends "minimal" | "representation" = "minimal",
115
114
  > implements
116
115
  ExecutableBuilder<
117
- ReturnPreference extends "minimal" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>
116
+ ReturnPreference extends "minimal"
117
+ ? { updatedCount: number }
118
+ : InferSchemaOutputFromFMTable<Occ>
118
119
  >
119
120
  {
120
121
  private databaseName: string;
@@ -183,9 +184,13 @@ export class ExecutableUpdateBuilder<
183
184
  }
184
185
 
185
186
  async execute(
186
- options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },
187
+ options?: ExecuteMethodOptions<ExecuteOptions>,
187
188
  ): Promise<
188
- Result<ReturnPreference extends "minimal" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>
189
+ Result<
190
+ ReturnPreference extends "minimal"
191
+ ? { updatedCount: number }
192
+ : InferSchemaOutputFromFMTable<Occ>
193
+ >
189
194
  > {
190
195
  // Merge database-level useEntityIds with per-request options
191
196
  const mergedOptions = this.mergeExecuteOptions(options);
@@ -198,7 +203,7 @@ export class ExecutableUpdateBuilder<
198
203
  if (this.table) {
199
204
  const baseTableConfig = getBaseTableConfig(this.table);
200
205
  const inputSchema = baseTableConfig.inputSchema;
201
-
206
+
202
207
  try {
203
208
  validatedData = await validateAndTransformInput(this.data, inputSchema);
204
209
  } catch (error) {
@@ -352,7 +357,11 @@ export class ExecutableUpdateBuilder<
352
357
  response: Response,
353
358
  options?: ExecuteOptions,
354
359
  ): Promise<
355
- Result<ReturnPreference extends "minimal" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>
360
+ Result<
361
+ ReturnPreference extends "minimal"
362
+ ? { updatedCount: number }
363
+ : InferSchemaOutputFromFMTable<Occ>
364
+ >
356
365
  > {
357
366
  // Check for error responses (important for batch operations)
358
367
  if (!response.ok) {
package/src/index.ts CHANGED
@@ -74,25 +74,14 @@ export type {
74
74
  BatchResult,
75
75
  BatchItemResult,
76
76
  InferSchemaType,
77
- InsertData,
78
- UpdateData,
79
77
  ODataRecordMetadata,
80
78
  Metadata,
79
+ FetchHandler,
80
+ ExecuteMethodOptions,
81
+ ExecuteOptions,
81
82
  } from "./types";
82
83
 
83
- // Filter types
84
- export type {
85
- Filter,
86
- TypedFilter,
87
- FieldFilter,
88
- StringOperators,
89
- NumberOperators,
90
- BooleanOperators,
91
- DateOperators,
92
- LogicalFilter,
93
- } from "./filter-types";
94
-
95
- // Re-export ffetch errors
84
+ // Re-export ffetch errors and types
96
85
  export {
97
86
  TimeoutError,
98
87
  AbortError,
@@ -101,6 +90,8 @@ export {
101
90
  CircuitOpenError,
102
91
  } from "@fetchkit/ffetch";
103
92
 
93
+ export type { FFetchOptions } from "@fetchkit/ffetch";
94
+
104
95
  // Export our errors
105
96
  export {
106
97
  FMODataError,