@proofkit/fmodata 0.1.0-alpha.9 → 0.1.0-beta.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +651 -449
  3. package/dist/esm/client/batch-builder.d.ts +10 -9
  4. package/dist/esm/client/batch-builder.js +119 -56
  5. package/dist/esm/client/batch-builder.js.map +1 -1
  6. package/dist/esm/client/batch-request.js +16 -21
  7. package/dist/esm/client/batch-request.js.map +1 -1
  8. package/dist/esm/client/builders/default-select.d.ts +10 -0
  9. package/dist/esm/client/builders/default-select.js +41 -0
  10. package/dist/esm/client/builders/default-select.js.map +1 -0
  11. package/dist/esm/client/builders/expand-builder.d.ts +45 -0
  12. package/dist/esm/client/builders/expand-builder.js +185 -0
  13. package/dist/esm/client/builders/expand-builder.js.map +1 -0
  14. package/dist/esm/client/builders/index.d.ts +9 -0
  15. package/dist/esm/client/builders/query-string-builder.d.ts +18 -0
  16. package/dist/esm/client/builders/query-string-builder.js +21 -0
  17. package/dist/esm/client/builders/query-string-builder.js.map +1 -0
  18. package/dist/esm/client/builders/response-processor.d.ts +43 -0
  19. package/dist/esm/client/builders/response-processor.js +175 -0
  20. package/dist/esm/client/builders/response-processor.js.map +1 -0
  21. package/dist/esm/client/builders/select-mixin.d.ts +25 -0
  22. package/dist/esm/client/builders/select-mixin.js +28 -0
  23. package/dist/esm/client/builders/select-mixin.js.map +1 -0
  24. package/dist/esm/client/builders/select-utils.d.ts +18 -0
  25. package/dist/esm/client/builders/select-utils.js +30 -0
  26. package/dist/esm/client/builders/select-utils.js.map +1 -0
  27. package/dist/esm/client/builders/shared-types.d.ts +40 -0
  28. package/dist/esm/client/builders/table-utils.d.ts +35 -0
  29. package/dist/esm/client/builders/table-utils.js +44 -0
  30. package/dist/esm/client/builders/table-utils.js.map +1 -0
  31. package/dist/esm/client/database.d.ts +34 -22
  32. package/dist/esm/client/database.js +48 -84
  33. package/dist/esm/client/database.js.map +1 -1
  34. package/dist/esm/client/delete-builder.d.ts +25 -30
  35. package/dist/esm/client/delete-builder.js +45 -30
  36. package/dist/esm/client/delete-builder.js.map +1 -1
  37. package/dist/esm/client/entity-set.d.ts +35 -43
  38. package/dist/esm/client/entity-set.js +110 -52
  39. package/dist/esm/client/entity-set.js.map +1 -1
  40. package/dist/esm/client/error-parser.d.ts +12 -0
  41. package/dist/esm/client/error-parser.js +25 -0
  42. package/dist/esm/client/error-parser.js.map +1 -0
  43. package/dist/esm/client/filemaker-odata.d.ts +26 -7
  44. package/dist/esm/client/filemaker-odata.js +65 -42
  45. package/dist/esm/client/filemaker-odata.js.map +1 -1
  46. package/dist/esm/client/insert-builder.d.ts +19 -24
  47. package/dist/esm/client/insert-builder.js +94 -58
  48. package/dist/esm/client/insert-builder.js.map +1 -1
  49. package/dist/esm/client/query/expand-builder.d.ts +35 -0
  50. package/dist/esm/client/query/index.d.ts +4 -0
  51. package/dist/esm/client/query/query-builder.d.ts +132 -0
  52. package/dist/esm/client/query/query-builder.js +456 -0
  53. package/dist/esm/client/query/query-builder.js.map +1 -0
  54. package/dist/esm/client/query/response-processor.d.ts +25 -0
  55. package/dist/esm/client/query/types.d.ts +77 -0
  56. package/dist/esm/client/query/url-builder.d.ts +71 -0
  57. package/dist/esm/client/query/url-builder.js +100 -0
  58. package/dist/esm/client/query/url-builder.js.map +1 -0
  59. package/dist/esm/client/query-builder.d.ts +2 -115
  60. package/dist/esm/client/record-builder.d.ts +108 -36
  61. package/dist/esm/client/record-builder.js +284 -119
  62. package/dist/esm/client/record-builder.js.map +1 -1
  63. package/dist/esm/client/response-processor.d.ts +4 -9
  64. package/dist/esm/client/sanitize-json.d.ts +35 -0
  65. package/dist/esm/client/sanitize-json.js +27 -0
  66. package/dist/esm/client/sanitize-json.js.map +1 -0
  67. package/dist/esm/client/schema-manager.d.ts +5 -5
  68. package/dist/esm/client/schema-manager.js +45 -31
  69. package/dist/esm/client/schema-manager.js.map +1 -1
  70. package/dist/esm/client/update-builder.d.ts +34 -40
  71. package/dist/esm/client/update-builder.js +99 -58
  72. package/dist/esm/client/update-builder.js.map +1 -1
  73. package/dist/esm/client/webhook-builder.d.ts +126 -0
  74. package/dist/esm/client/webhook-builder.js +189 -0
  75. package/dist/esm/client/webhook-builder.js.map +1 -0
  76. package/dist/esm/errors.d.ts +19 -2
  77. package/dist/esm/errors.js +39 -4
  78. package/dist/esm/errors.js.map +1 -1
  79. package/dist/esm/index.d.ts +10 -8
  80. package/dist/esm/index.js +40 -10
  81. package/dist/esm/index.js.map +1 -1
  82. package/dist/esm/logger.d.ts +47 -0
  83. package/dist/esm/logger.js +69 -0
  84. package/dist/esm/logger.js.map +1 -0
  85. package/dist/esm/logger.test.d.ts +1 -0
  86. package/dist/esm/orm/column.d.ts +62 -0
  87. package/dist/esm/orm/column.js +63 -0
  88. package/dist/esm/orm/column.js.map +1 -0
  89. package/dist/esm/orm/field-builders.d.ts +164 -0
  90. package/dist/esm/orm/field-builders.js +158 -0
  91. package/dist/esm/orm/field-builders.js.map +1 -0
  92. package/dist/esm/orm/index.d.ts +5 -0
  93. package/dist/esm/orm/operators.d.ts +173 -0
  94. package/dist/esm/orm/operators.js +260 -0
  95. package/dist/esm/orm/operators.js.map +1 -0
  96. package/dist/esm/orm/table.d.ts +355 -0
  97. package/dist/esm/orm/table.js +202 -0
  98. package/dist/esm/orm/table.js.map +1 -0
  99. package/dist/esm/transform.d.ts +20 -21
  100. package/dist/esm/transform.js +44 -45
  101. package/dist/esm/transform.js.map +1 -1
  102. package/dist/esm/types.d.ts +96 -30
  103. package/dist/esm/types.js +7 -0
  104. package/dist/esm/types.js.map +1 -0
  105. package/dist/esm/validation.d.ts +22 -12
  106. package/dist/esm/validation.js +132 -85
  107. package/dist/esm/validation.js.map +1 -1
  108. package/package.json +28 -20
  109. package/src/client/batch-builder.ts +153 -89
  110. package/src/client/batch-request.ts +25 -41
  111. package/src/client/builders/default-select.ts +75 -0
  112. package/src/client/builders/expand-builder.ts +246 -0
  113. package/src/client/builders/index.ts +11 -0
  114. package/src/client/builders/query-string-builder.ts +46 -0
  115. package/src/client/builders/response-processor.ts +279 -0
  116. package/src/client/builders/select-mixin.ts +65 -0
  117. package/src/client/builders/select-utils.ts +59 -0
  118. package/src/client/builders/shared-types.ts +45 -0
  119. package/src/client/builders/table-utils.ts +83 -0
  120. package/src/client/database.ts +89 -183
  121. package/src/client/delete-builder.ts +74 -84
  122. package/src/client/entity-set.ts +266 -293
  123. package/src/client/error-parser.ts +41 -0
  124. package/src/client/filemaker-odata.ts +98 -66
  125. package/src/client/insert-builder.ts +157 -118
  126. package/src/client/query/expand-builder.ts +160 -0
  127. package/src/client/query/index.ts +14 -0
  128. package/src/client/query/query-builder.ts +729 -0
  129. package/src/client/query/response-processor.ts +226 -0
  130. package/src/client/query/types.ts +126 -0
  131. package/src/client/query/url-builder.ts +151 -0
  132. package/src/client/query-builder.ts +10 -1455
  133. package/src/client/record-builder.ts +575 -240
  134. package/src/client/response-processor.ts +15 -42
  135. package/src/client/sanitize-json.ts +64 -0
  136. package/src/client/schema-manager.ts +61 -76
  137. package/src/client/update-builder.ts +161 -143
  138. package/src/client/webhook-builder.ts +265 -0
  139. package/src/errors.ts +49 -16
  140. package/src/index.ts +99 -54
  141. package/src/logger.test.ts +34 -0
  142. package/src/logger.ts +116 -0
  143. package/src/orm/column.ts +106 -0
  144. package/src/orm/field-builders.ts +250 -0
  145. package/src/orm/index.ts +61 -0
  146. package/src/orm/operators.ts +473 -0
  147. package/src/orm/table.ts +741 -0
  148. package/src/transform.ts +90 -70
  149. package/src/types.ts +154 -113
  150. package/src/validation.ts +200 -115
  151. package/dist/esm/client/base-table.d.ts +0 -125
  152. package/dist/esm/client/base-table.js +0 -57
  153. package/dist/esm/client/base-table.js.map +0 -1
  154. package/dist/esm/client/query-builder.js +0 -896
  155. package/dist/esm/client/query-builder.js.map +0 -1
  156. package/dist/esm/client/table-occurrence.d.ts +0 -72
  157. package/dist/esm/client/table-occurrence.js +0 -74
  158. package/dist/esm/client/table-occurrence.js.map +0 -1
  159. package/dist/esm/filter-types.d.ts +0 -76
  160. package/src/client/base-table.ts +0 -175
  161. package/src/client/query-builder.ts.bak +0 -1457
  162. package/src/client/table-occurrence.ts +0 -175
  163. package/src/filter-types.ts +0 -97
package/src/types.ts CHANGED
@@ -1,18 +1,21 @@
1
- import { type FFetchOptions } from "@fetchkit/ffetch";
1
+ import type { FFetchOptions } from "@fetchkit/ffetch";
2
2
  import type { StandardSchemaV1 } from "@standard-schema/spec";
3
+ import type { InternalLogger } from "./logger";
3
4
 
4
5
  export type Auth = { username: string; password: string } | { apiKey: string };
5
6
 
6
7
  export interface ExecutableBuilder<T> {
7
8
  execute(): Promise<Result<T>>;
9
+ // biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value
8
10
  getRequestConfig(): { method: string; url: string; body?: any };
9
11
 
10
12
  /**
11
13
  * Convert this builder to a native Request object for batch processing.
12
14
  * @param baseUrl - The base URL for the OData service
15
+ * @param options - Optional execution options (e.g., includeODataAnnotations)
13
16
  * @returns A native Request object
14
17
  */
15
- toRequest(baseUrl: string): Request;
18
+ toRequest(baseUrl: string, options?: ExecuteOptions): Request;
16
19
 
17
20
  /**
18
21
  * Process a raw Response object into a typed Result.
@@ -21,29 +24,33 @@ export interface ExecutableBuilder<T> {
21
24
  * @param options - Optional execution options (e.g., skipValidation, includeODataAnnotations)
22
25
  * @returns A typed Result with the builder's expected return type
23
26
  */
24
- processResponse(
25
- response: Response,
26
- options?: ExecuteOptions,
27
- ): Promise<Result<T>>;
27
+ processResponse(response: Response, options?: ExecuteOptions): Promise<Result<T>>;
28
28
  }
29
29
 
30
30
  export interface ExecutionContext {
31
31
  _makeRequest<T>(
32
32
  url: string,
33
- options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },
33
+ options?: RequestInit &
34
+ FFetchOptions & {
35
+ useEntityIds?: boolean;
36
+ includeSpecialColumns?: boolean;
37
+ },
34
38
  ): Promise<Result<T>>;
35
39
  _setUseEntityIds?(useEntityIds: boolean): void;
36
40
  _getUseEntityIds?(): boolean;
41
+ _setIncludeSpecialColumns?(includeSpecialColumns: boolean): void;
42
+ _getIncludeSpecialColumns?(): boolean;
37
43
  _getBaseUrl?(): string;
44
+ _getLogger?(): InternalLogger;
38
45
  }
39
46
 
40
47
  export type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = {
41
- [K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output>
42
- ? Output
43
- : never;
48
+ // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer
49
+ [K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output> ? Output : never;
44
50
  };
45
51
 
46
- export type WithSystemFields<T> =
52
+ export type WithSpecialColumns<T> =
53
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any record shape
47
54
  T extends Record<string, any>
48
55
  ? T & {
49
56
  ROWID: number;
@@ -51,26 +58,21 @@ export type WithSystemFields<T> =
51
58
  }
52
59
  : never;
53
60
 
54
- // Helper type to exclude system fields from a union of keys
55
- export type ExcludeSystemFields<T extends keyof any> = Exclude<
56
- T,
57
- "ROWID" | "ROWMODID"
58
- >;
59
-
60
- // Helper type to omit system fields from an object type
61
- export type OmitSystemFields<T> = Omit<T, "ROWID" | "ROWMODID">;
61
+ // Helper type to exclude special columns from a union of keys
62
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any key type
63
+ export type ExcludeSystemFields<T extends keyof any> = Exclude<T, "ROWID" | "ROWMODID">;
62
64
 
63
65
  // OData record metadata fields (present on each record)
64
- export type ODataRecordMetadata = {
66
+ export interface ODataRecordMetadata {
65
67
  "@id": string;
66
68
  "@editLink": string;
67
- };
69
+ }
68
70
 
69
71
  // OData response wrapper (top-level, internal use only)
70
- export type ODataListResponse<T> = {
72
+ export interface ODataListResponse<T> {
71
73
  "@context": string;
72
74
  value: (T & ODataRecordMetadata)[];
73
- };
75
+ }
74
76
 
75
77
  export type ODataSingleResponse<T> = T &
76
78
  ODataRecordMetadata & {
@@ -78,51 +80,62 @@ export type ODataSingleResponse<T> = T &
78
80
  };
79
81
 
80
82
  // OData response for single field values
81
- export type ODataFieldResponse<T> = {
83
+ export interface ODataFieldResponse<T> {
82
84
  "@context": string;
83
85
  value: T;
84
- };
86
+ }
85
87
 
86
88
  // Result pattern for execute responses
87
89
  export type Result<T, E = import("./errors").FMODataErrorType> =
88
90
  | { data: T; error: undefined }
89
91
  | { data: undefined; error: E };
90
92
 
93
+ // Batch operation result types
94
+ export interface BatchItemResult<T> {
95
+ data: T | undefined;
96
+ error: import("./errors").FMODataErrorType | undefined;
97
+ status: number; // HTTP status code (0 for truncated)
98
+ }
99
+
100
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any tuple type
101
+ export interface BatchResult<T extends readonly any[]> {
102
+ results: { [K in keyof T]: BatchItemResult<T[K]> };
103
+ successCount: number;
104
+ errorCount: number;
105
+ truncated: boolean;
106
+ firstErrorIndex: number | null;
107
+ }
108
+
91
109
  // Make specific keys required, rest optional
92
- export type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> &
93
- Required<Pick<T, Keys>>;
110
+ export type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> & Required<Pick<T, Keys>>;
94
111
 
95
112
  // Extract keys from schema where validator doesn't allow null/undefined (auto-required fields)
96
- export type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> =
97
- {
98
- [K in keyof Schema]: Extract<
99
- StandardSchemaV1.InferOutput<Schema[K]>,
100
- null | undefined
101
- > extends never
102
- ? K
103
- : never;
104
- }[keyof Schema];
113
+ export type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> = {
114
+ [K in keyof Schema]: Extract<StandardSchemaV1.InferOutput<Schema[K]>, null | undefined> extends never ? K : never;
115
+ }[keyof Schema];
105
116
 
106
117
  // Helper type to compute excluded fields (readOnly fields + idField)
107
118
  export type ExcludedFields<
119
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any key type
108
120
  IdField extends keyof any | undefined,
121
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any array type
109
122
  ReadOnly extends readonly any[],
123
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any key type
110
124
  > = IdField extends keyof any ? IdField | ReadOnly[number] : ReadOnly[number];
111
125
 
112
126
  // Helper type for InsertData computation
113
- type ComputeInsertData<
127
+ type _ComputeInsertData<
114
128
  Schema extends Record<string, StandardSchemaV1>,
115
129
  IdField extends keyof Schema | undefined,
130
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any array type
116
131
  Required extends readonly any[],
132
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any array type
117
133
  ReadOnly extends readonly any[],
118
134
  > = [Required[number]] extends [keyof InferSchemaType<Schema>]
119
135
  ? Required extends readonly (keyof InferSchemaType<Schema>)[]
120
136
  ? MakeFieldsRequired<
121
137
  Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,
122
- Exclude<
123
- AutoRequiredKeys<Schema> | Required[number],
124
- ExcludedFields<IdField, ReadOnly>
125
- >
138
+ Exclude<AutoRequiredKeys<Schema> | Required[number], ExcludedFields<IdField, ReadOnly>>
126
139
  >
127
140
  : MakeFieldsRequired<
128
141
  Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,
@@ -133,77 +146,59 @@ type ComputeInsertData<
133
146
  Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>
134
147
  >;
135
148
 
136
- // Extract insert data type from BaseTable
137
- // Auto-infers required fields from validator nullability + user-specified required fields
138
- // Excludes readOnly fields and idField
139
- export type InsertData<BT> = BT extends import("./client/base-table").BaseTable<
140
- any,
141
- any,
142
- any,
143
- any
144
- >
145
- ? BT extends {
146
- schema: infer Schema;
147
- idField?: infer IdField;
148
- required?: infer Required;
149
- readOnly?: infer ReadOnly;
150
- }
151
- ? Schema extends Record<string, StandardSchemaV1>
152
- ? IdField extends keyof Schema | undefined
153
- ? Required extends readonly any[]
154
- ? ReadOnly extends readonly any[]
155
- ? ComputeInsertData<
156
- Schema,
157
- Extract<IdField, keyof Schema | undefined>,
158
- Required,
159
- ReadOnly
160
- >
161
- : Partial<Record<string, any>>
162
- : Partial<Record<string, any>>
163
- : Partial<Record<string, any>>
164
- : Partial<Record<string, any>>
165
- : Partial<Record<string, any>>
166
- : Partial<Record<string, any>>;
167
-
168
- // Extract update data type from BaseTable
169
- // All fields are optional for updates, excludes readOnly fields and idField
170
- export type UpdateData<BT> = BT extends import("./client/base-table").BaseTable<
171
- any,
172
- any,
173
- any,
174
- any
175
- >
176
- ? BT extends {
177
- schema: infer Schema;
178
- idField?: infer IdField;
179
- readOnly?: infer ReadOnly;
180
- }
181
- ? Schema extends Record<string, StandardSchemaV1>
182
- ? IdField extends keyof Schema | undefined
183
- ? ReadOnly extends readonly any[]
184
- ? Partial<
185
- Omit<
186
- InferSchemaType<Schema>,
187
- ExcludedFields<
188
- Extract<IdField, keyof Schema | undefined>,
189
- ReadOnly
190
- >
191
- >
192
- >
193
- : Partial<Record<string, any>>
194
- : Partial<Record<string, any>>
195
- : Partial<Record<string, any>>
196
- : Partial<Record<string, any>>
197
- : Partial<Record<string, any>>;
198
-
199
- export type ExecuteOptions = {
149
+ export interface ExecuteOptions {
200
150
  includeODataAnnotations?: boolean;
201
151
  skipValidation?: boolean;
202
152
  /**
203
153
  * Overrides the default behavior of the database to use entity IDs (rather than field names) in THIS REQUEST ONLY
204
154
  */
205
155
  useEntityIds?: boolean;
206
- };
156
+ /**
157
+ * Overrides the default behavior of the database to include special columns (ROWID and ROWMODID) in THIS REQUEST ONLY.
158
+ * Note: Special columns are only included when there is no $select query.
159
+ */
160
+ includeSpecialColumns?: boolean;
161
+ }
162
+
163
+ /**
164
+ * Type for the fetchHandler callback function.
165
+ * This is a convenience type export that matches the fetchHandler signature in FFetchOptions.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * import type { FetchHandler } from '@proofkit/fmodata';
170
+ *
171
+ * const myFetchHandler: FetchHandler = (input, init) => {
172
+ * console.log('Custom fetch:', input);
173
+ * return fetch(input, init);
174
+ * };
175
+ *
176
+ * await query.execute({
177
+ * fetchHandler: myFetchHandler
178
+ * });
179
+ * ```
180
+ */
181
+ export type FetchHandler = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
182
+
183
+ /**
184
+ * Combined type for execute() method options.
185
+ *
186
+ * Uses FFetchOptions from @fetchkit/ffetch to ensure proper type inference.
187
+ * FFetchOptions is re-exported in the package to ensure type availability in consuming packages.
188
+ */
189
+ export type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> = RequestInit &
190
+ FFetchOptions &
191
+ ExecuteOptions &
192
+ EO;
193
+
194
+ /**
195
+ * Get the Accept header value based on includeODataAnnotations option
196
+ * @param includeODataAnnotations - Whether to include OData annotations
197
+ * @returns Accept header value
198
+ */
199
+ export function getAcceptHeader(includeODataAnnotations?: boolean): string {
200
+ return includeODataAnnotations === true ? "application/json" : "application/json;odata.metadata=none";
201
+ }
207
202
 
208
203
  export type ConditionallyWithODataAnnotations<
209
204
  T,
@@ -215,7 +210,53 @@ export type ConditionallyWithODataAnnotations<
215
210
  }
216
211
  : T;
217
212
 
218
- // Helper type to extract schema from a TableOccurrence
213
+ /**
214
+ * Normalizes includeSpecialColumns with a database-level default.
215
+ * Uses distributive conditional types to handle unions correctly.
216
+ * @template IncludeSpecialColumns - The includeSpecialColumns value from execute options
217
+ * @template DatabaseDefault - The database-level includeSpecialColumns setting (defaults to false)
218
+ */
219
+ export type NormalizeIncludeSpecialColumns<
220
+ IncludeSpecialColumns extends boolean | undefined,
221
+ DatabaseDefault extends boolean = false,
222
+ > = [IncludeSpecialColumns] extends [true] ? true : [IncludeSpecialColumns] extends [false] ? false : DatabaseDefault; // When undefined, use database-level default
223
+
224
+ /**
225
+ * Conditionally adds ROWID and ROWMODID special columns to a type.
226
+ * Special columns are only included when:
227
+ * - includeSpecialColumns is true AND
228
+ * - hasSelect is false (no $select query was applied) AND
229
+ * - T is an object type (not a primitive like string or number)
230
+ *
231
+ * Handles both single objects and arrays of objects.
232
+ */
233
+ export type ConditionallyWithSpecialColumns<
234
+ T,
235
+ IncludeSpecialColumns extends boolean,
236
+ HasSelect extends boolean,
237
+ > = IncludeSpecialColumns extends true
238
+ ? HasSelect extends false
239
+ ? // Handle array types
240
+ T extends readonly (infer U)[]
241
+ ? // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any record shape
242
+ U extends Record<string, any>
243
+ ? (U & {
244
+ ROWID: number;
245
+ ROWMODID: number;
246
+ })[]
247
+ : T
248
+ : // Handle single object types
249
+ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any record shape
250
+ T extends Record<string, any>
251
+ ? T & {
252
+ ROWID: number;
253
+ ROWMODID: number;
254
+ }
255
+ : T // Don't add special columns to primitives (e.g., single field queries)
256
+ : T
257
+ : T;
258
+
259
+ // Helper type to extract schema from a FMTable
219
260
  export type ExtractSchemaFromOccurrence<Occ> = Occ extends {
220
261
  baseTable: { schema: infer S };
221
262
  }
@@ -224,14 +265,14 @@ export type ExtractSchemaFromOccurrence<Occ> = Occ extends {
224
265
  : Record<string, StandardSchemaV1>
225
266
  : Record<string, StandardSchemaV1>;
226
267
 
227
- export type GenericFieldMetadata = {
268
+ export interface GenericFieldMetadata {
228
269
  $Nullable?: boolean;
229
270
  "@Index"?: boolean;
230
271
  "@Calculation"?: boolean;
231
272
  "@Summary"?: boolean;
232
273
  "@Global"?: boolean;
233
274
  "@Org.OData.Core.V1.Permissions"?: "Org.OData.Core.V1.Permission@Read";
234
- };
275
+ }
235
276
 
236
277
  export type StringFieldMetadata = GenericFieldMetadata & {
237
278
  $Type: "Edm.String";
@@ -260,13 +301,13 @@ export type DateTimeOffsetFieldMetadata = GenericFieldMetadata & {
260
301
  "@VersionId"?: boolean;
261
302
  };
262
303
 
263
- export type StreamFieldMetadata = {
304
+ export interface StreamFieldMetadata {
264
305
  $Type: "Edm.Stream";
265
306
  $Nullable?: boolean;
266
307
  "@EnclosedPath": string;
267
308
  "@ExternalOpenPath": string;
268
309
  "@ExternalSecurePath"?: string;
269
- };
310
+ }
270
311
 
271
312
  export type FieldMetadata =
272
313
  | StringFieldMetadata
@@ -281,9 +322,9 @@ export type EntityType = {
281
322
  $Key: string[];
282
323
  } & Record<string, FieldMetadata>;
283
324
 
284
- export type EntitySet = {
325
+ export interface EntitySet {
285
326
  $Kind: "EntitySet";
286
327
  $Type: string;
287
- };
328
+ }
288
329
 
289
330
  export type Metadata = Record<string, EntityType | EntitySet>;