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

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 (143) hide show
  1. package/README.md +489 -334
  2. package/dist/esm/client/batch-builder.d.ts +7 -4
  3. package/dist/esm/client/batch-builder.js +84 -25
  4. package/dist/esm/client/batch-builder.js.map +1 -1
  5. package/dist/esm/client/builders/default-select.d.ts +7 -0
  6. package/dist/esm/client/builders/default-select.js +42 -0
  7. package/dist/esm/client/builders/default-select.js.map +1 -0
  8. package/dist/esm/client/builders/expand-builder.d.ts +43 -0
  9. package/dist/esm/client/builders/expand-builder.js +173 -0
  10. package/dist/esm/client/builders/expand-builder.js.map +1 -0
  11. package/dist/esm/client/builders/index.d.ts +8 -0
  12. package/dist/esm/client/builders/query-string-builder.d.ts +15 -0
  13. package/dist/esm/client/builders/query-string-builder.js +25 -0
  14. package/dist/esm/client/builders/query-string-builder.js.map +1 -0
  15. package/dist/esm/client/builders/response-processor.d.ts +39 -0
  16. package/dist/esm/client/builders/response-processor.js +170 -0
  17. package/dist/esm/client/builders/response-processor.js.map +1 -0
  18. package/dist/esm/client/builders/select-mixin.d.ts +31 -0
  19. package/dist/esm/client/builders/select-mixin.js +30 -0
  20. package/dist/esm/client/builders/select-mixin.js.map +1 -0
  21. package/dist/esm/client/builders/select-utils.d.ts +8 -0
  22. package/dist/esm/client/builders/select-utils.js +15 -0
  23. package/dist/esm/client/builders/select-utils.js.map +1 -0
  24. package/dist/esm/client/builders/shared-types.d.ts +39 -0
  25. package/dist/esm/client/builders/table-utils.d.ts +35 -0
  26. package/dist/esm/client/builders/table-utils.js +45 -0
  27. package/dist/esm/client/builders/table-utils.js.map +1 -0
  28. package/dist/esm/client/database.d.ts +3 -22
  29. package/dist/esm/client/database.js +14 -76
  30. package/dist/esm/client/database.js.map +1 -1
  31. package/dist/esm/client/delete-builder.d.ts +11 -15
  32. package/dist/esm/client/delete-builder.js +26 -26
  33. package/dist/esm/client/delete-builder.js.map +1 -1
  34. package/dist/esm/client/entity-set.d.ts +32 -32
  35. package/dist/esm/client/entity-set.js +92 -69
  36. package/dist/esm/client/entity-set.js.map +1 -1
  37. package/dist/esm/client/error-parser.d.ts +12 -0
  38. package/dist/esm/client/error-parser.js +30 -0
  39. package/dist/esm/client/error-parser.js.map +1 -0
  40. package/dist/esm/client/filemaker-odata.d.ts +2 -4
  41. package/dist/esm/client/filemaker-odata.js +1 -5
  42. package/dist/esm/client/filemaker-odata.js.map +1 -1
  43. package/dist/esm/client/insert-builder.d.ts +7 -9
  44. package/dist/esm/client/insert-builder.js +70 -24
  45. package/dist/esm/client/insert-builder.js.map +1 -1
  46. package/dist/esm/client/query/expand-builder.d.ts +35 -0
  47. package/dist/esm/client/query/index.d.ts +3 -0
  48. package/dist/esm/client/query/query-builder.d.ts +134 -0
  49. package/dist/esm/client/query/query-builder.js +505 -0
  50. package/dist/esm/client/query/query-builder.js.map +1 -0
  51. package/dist/esm/client/query/response-processor.d.ts +22 -0
  52. package/dist/esm/client/query/types.d.ts +52 -0
  53. package/dist/esm/client/query/url-builder.d.ts +71 -0
  54. package/dist/esm/client/query/url-builder.js +107 -0
  55. package/dist/esm/client/query/url-builder.js.map +1 -0
  56. package/dist/esm/client/query-builder.d.ts +1 -111
  57. package/dist/esm/client/record-builder.d.ts +56 -63
  58. package/dist/esm/client/record-builder.js +158 -297
  59. package/dist/esm/client/record-builder.js.map +1 -1
  60. package/dist/esm/client/response-processor.d.ts +3 -3
  61. package/dist/esm/client/update-builder.d.ts +16 -21
  62. package/dist/esm/client/update-builder.js +56 -30
  63. package/dist/esm/client/update-builder.js.map +1 -1
  64. package/dist/esm/errors.d.ts +8 -1
  65. package/dist/esm/errors.js +17 -0
  66. package/dist/esm/errors.js.map +1 -1
  67. package/dist/esm/index.d.ts +3 -7
  68. package/dist/esm/index.js +37 -8
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/orm/column.d.ts +45 -0
  71. package/dist/esm/orm/column.js +59 -0
  72. package/dist/esm/orm/column.js.map +1 -0
  73. package/dist/esm/orm/field-builders.d.ts +154 -0
  74. package/dist/esm/orm/field-builders.js +152 -0
  75. package/dist/esm/orm/field-builders.js.map +1 -0
  76. package/dist/esm/orm/index.d.ts +4 -0
  77. package/dist/esm/orm/operators.d.ts +175 -0
  78. package/dist/esm/orm/operators.js +221 -0
  79. package/dist/esm/orm/operators.js.map +1 -0
  80. package/dist/esm/orm/table.d.ts +341 -0
  81. package/dist/esm/orm/table.js +211 -0
  82. package/dist/esm/orm/table.js.map +1 -0
  83. package/dist/esm/transform.d.ts +20 -21
  84. package/dist/esm/transform.js +34 -34
  85. package/dist/esm/transform.js.map +1 -1
  86. package/dist/esm/types.d.ts +16 -13
  87. package/dist/esm/types.js.map +1 -1
  88. package/dist/esm/validation.d.ts +14 -4
  89. package/dist/esm/validation.js +45 -1
  90. package/dist/esm/validation.js.map +1 -1
  91. package/package.json +20 -17
  92. package/src/client/batch-builder.ts +100 -32
  93. package/src/client/builders/default-select.ts +69 -0
  94. package/src/client/builders/expand-builder.ts +236 -0
  95. package/src/client/builders/index.ts +11 -0
  96. package/src/client/builders/query-string-builder.ts +41 -0
  97. package/src/client/builders/response-processor.ts +273 -0
  98. package/src/client/builders/select-mixin.ts +74 -0
  99. package/src/client/builders/select-utils.ts +34 -0
  100. package/src/client/builders/shared-types.ts +41 -0
  101. package/src/client/builders/table-utils.ts +87 -0
  102. package/src/client/database.ts +19 -160
  103. package/src/client/delete-builder.ts +46 -51
  104. package/src/client/entity-set.ts +227 -302
  105. package/src/client/error-parser.ts +59 -0
  106. package/src/client/filemaker-odata.ts +3 -14
  107. package/src/client/insert-builder.ts +124 -43
  108. package/src/client/query/expand-builder.ts +164 -0
  109. package/src/client/query/index.ts +13 -0
  110. package/src/client/query/query-builder.ts +816 -0
  111. package/src/client/query/response-processor.ts +244 -0
  112. package/src/client/query/types.ts +102 -0
  113. package/src/client/query/url-builder.ts +179 -0
  114. package/src/client/query-builder.ts +8 -1454
  115. package/src/client/record-builder.ts +325 -585
  116. package/src/client/response-processor.ts +4 -5
  117. package/src/client/update-builder.ts +102 -73
  118. package/src/errors.ts +22 -1
  119. package/src/index.ts +55 -5
  120. package/src/orm/column.ts +78 -0
  121. package/src/orm/field-builders.ts +296 -0
  122. package/src/orm/index.ts +60 -0
  123. package/src/orm/operators.ts +428 -0
  124. package/src/orm/table.ts +759 -0
  125. package/src/transform.ts +62 -48
  126. package/src/types.ts +20 -63
  127. package/src/validation.ts +76 -4
  128. package/LICENSE.md +0 -21
  129. package/dist/esm/client/base-table.d.ts +0 -128
  130. package/dist/esm/client/base-table.js +0 -57
  131. package/dist/esm/client/base-table.js.map +0 -1
  132. package/dist/esm/client/build-occurrences.d.ts +0 -74
  133. package/dist/esm/client/build-occurrences.js +0 -31
  134. package/dist/esm/client/build-occurrences.js.map +0 -1
  135. package/dist/esm/client/query-builder.js +0 -900
  136. package/dist/esm/client/query-builder.js.map +0 -1
  137. package/dist/esm/client/table-occurrence.d.ts +0 -86
  138. package/dist/esm/client/table-occurrence.js +0 -58
  139. package/dist/esm/client/table-occurrence.js.map +0 -1
  140. package/src/client/base-table.ts +0 -178
  141. package/src/client/build-occurrences.ts +0 -155
  142. package/src/client/query-builder.ts.bak +0 -1457
  143. package/src/client/table-occurrence.ts +0 -156
@@ -0,0 +1,341 @@
1
+ import { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { FieldBuilder, ContainerDbType, FieldBuilder as FieldBuilderType } from './field-builders.js';
3
+ import { Column } from './column.js';
4
+ /**
5
+ * Extract the output type from a FieldBuilder.
6
+ * This is what you get when reading from the database.
7
+ *
8
+ * This type extracts the TOutput type parameter, which is set by readValidator()
9
+ * and represents the transformed/validated output type.
10
+ */
11
+ export type InferFieldOutput<F> = F extends FieldBuilder<infer TOutput, any, any, any> ? TOutput : never;
12
+ /**
13
+ * Extract the input type from a FieldBuilder.
14
+ * This is what you pass when writing to the database.
15
+ *
16
+ * This type extracts the TInput type parameter, which is set by writeValidator()
17
+ * and represents the transformed/validated input type.
18
+ */
19
+ type InferFieldInput<F> = F extends FieldBuilder<any, infer TInput, any, any> ? TInput : never;
20
+ /**
21
+ * Build a schema type from field builders (output/read types).
22
+ */
23
+ type InferSchemaFromFields<TFields extends Record<string, FieldBuilder<any, any, any, any>>> = {
24
+ [K in keyof TFields]: InferFieldOutput<TFields[K]>;
25
+ };
26
+ /**
27
+ * Build an input schema type from field builders (input/write types).
28
+ * Used for insert and update operations.
29
+ */
30
+ type InferInputSchemaFromFields<TFields extends Record<string, FieldBuilder<any, any, any, any>>> = {
31
+ [K in keyof TFields]: InferFieldInput<TFields[K]>;
32
+ };
33
+ /**
34
+ * Check if a field is a container field by inspecting its TDbType.
35
+ * Container fields have a branded TDbType that extends ContainerDbType.
36
+ */
37
+ type IsContainerField<F> = F extends FieldBuilder<any, any, infer TDbType, any> ? NonNullable<TDbType> extends ContainerDbType ? true : false : false;
38
+ /**
39
+ * Extract only selectable (non-container) field keys from a fields record.
40
+ * Container fields are excluded because they cannot be selected via $select in FileMaker OData.
41
+ */
42
+ type SelectableFieldKeys<TFields extends Record<string, FieldBuilder<any, any, any, any>>> = {
43
+ [K in keyof TFields]: IsContainerField<TFields[K]> extends true ? never : K;
44
+ }[keyof TFields];
45
+ /**
46
+ * Internal Symbols for table properties (hidden from IDE autocomplete).
47
+ * These are used to store internal configuration that shouldn't be visible
48
+ * when users access table columns.
49
+ * @internal - Not exported from public API, only accessible via FMTable.Symbol
50
+ */
51
+ declare const FMTableName: unique symbol;
52
+ declare const FMTableEntityId: unique symbol;
53
+ declare const FMTableSchema: unique symbol;
54
+ declare const FMTableFields: unique symbol;
55
+ declare const FMTableNavigationPaths: unique symbol;
56
+ declare const FMTableDefaultSelect: unique symbol;
57
+ declare const FMTableBaseTableConfig: unique symbol;
58
+ declare const FMTableUseEntityIds: unique symbol;
59
+ /**
60
+ * Base table class with Symbol-based internal properties.
61
+ * This follows the Drizzle ORM pattern where internal configuration
62
+ * is stored via Symbols, keeping it hidden from IDE autocomplete.
63
+ */
64
+ export declare class FMTable<TFields extends Record<string, FieldBuilder<any, any, any, any>> = any, TName extends string = string, TNavigationPaths extends readonly string[] = readonly string[]> {
65
+ /**
66
+ * Internal Symbols for accessing table metadata.
67
+ * @internal - Not intended for public use. Access table properties via columns instead.
68
+ */
69
+ static readonly Symbol: {
70
+ Name: symbol;
71
+ EntityId: symbol;
72
+ UseEntityIds: symbol;
73
+ Schema: symbol;
74
+ Fields: symbol;
75
+ NavigationPaths: symbol;
76
+ DefaultSelect: symbol;
77
+ BaseTableConfig: symbol;
78
+ };
79
+ /** @internal */
80
+ [FMTableName]: TName;
81
+ /** @internal */
82
+ [FMTableEntityId]?: `FMTID:${string}`;
83
+ /** @internal */
84
+ [FMTableUseEntityIds]?: boolean;
85
+ /** @internal */
86
+ [FMTableSchema]: StandardSchemaV1<any, InferSchemaFromFields<TFields>>;
87
+ /** @internal */
88
+ [FMTableFields]: TFields;
89
+ /** @internal */
90
+ [FMTableNavigationPaths]: TNavigationPaths;
91
+ /** @internal */
92
+ [FMTableDefaultSelect]: "all" | "schema" | Record<string, Column<any, TName>>;
93
+ /** @internal */
94
+ [FMTableBaseTableConfig]: {
95
+ schema: Record<keyof TFields, StandardSchemaV1>;
96
+ inputSchema?: Record<keyof TFields, StandardSchemaV1>;
97
+ idField?: keyof TFields;
98
+ required: readonly (keyof TFields)[];
99
+ readOnly: readonly (keyof TFields)[];
100
+ containerFields: readonly (keyof TFields)[];
101
+ fmfIds?: Record<keyof TFields, `FMFID:${string}`>;
102
+ };
103
+ constructor(config: {
104
+ name: TName;
105
+ entityId?: `FMTID:${string}`;
106
+ useEntityIds?: boolean;
107
+ schema: StandardSchemaV1<any, InferSchemaFromFields<TFields>>;
108
+ fields: TFields;
109
+ navigationPaths: TNavigationPaths;
110
+ defaultSelect: "all" | "schema" | Record<string, Column<any, TName>>;
111
+ baseTableConfig: {
112
+ schema: Record<keyof TFields, StandardSchemaV1>;
113
+ inputSchema?: Record<keyof TFields, StandardSchemaV1>;
114
+ idField?: keyof TFields;
115
+ required: readonly (keyof TFields)[];
116
+ readOnly: readonly (keyof TFields)[];
117
+ containerFields: readonly (keyof TFields)[];
118
+ fmfIds?: Record<keyof TFields, `FMFID:${string}`>;
119
+ };
120
+ });
121
+ }
122
+ /**
123
+ * Type helper to extract the column map from fields.
124
+ * Table name is baked into each column type for validation.
125
+ * Container fields are marked with IsContainer=true.
126
+ */
127
+ export type ColumnMap<TFields extends Record<string, FieldBuilder<any, any, any, any>>, TName extends string> = {
128
+ [K in keyof TFields]: Column<InferFieldOutput<TFields[K]>, TName, IsContainerField<TFields[K]>>;
129
+ };
130
+ /**
131
+ * Extract only selectable (non-container) columns from a table.
132
+ * This is used to prevent selecting container fields in queries.
133
+ */
134
+ export type SelectableColumnMap<TFields extends Record<string, FieldBuilder<any, any, any, any>>, TName extends string> = {
135
+ [K in SelectableFieldKeys<TFields>]: Column<InferFieldOutput<TFields[K]>, TName, false>;
136
+ };
137
+ /**
138
+ * Validates that a select object doesn't contain container field columns.
139
+ * Returns never if any container fields are found, otherwise returns the original type.
140
+ */
141
+ export type ValidateNoContainerFields<TSelect extends Record<string, Column<any, any, any>>> = {
142
+ [K in keyof TSelect]: TSelect[K] extends Column<any, any, true> ? never : TSelect[K];
143
+ } extends TSelect ? TSelect : {
144
+ [K in keyof TSelect]: TSelect[K] extends Column<any, any, true> ? "❌ Container fields cannot be selected. Use .getSingleField() instead." : TSelect[K];
145
+ };
146
+ /**
147
+ * Complete table type with both metadata (via Symbols) and column accessors.
148
+ * This is the return type of fmTableOccurrence - users see columns directly,
149
+ * but internal config is hidden via Symbols.
150
+ */
151
+ export type FMTableWithColumns<TFields extends Record<string, FieldBuilder<any, any, any, any>>, TName extends string, TNavigationPaths extends readonly string[] = readonly string[]> = FMTable<TFields, TName, TNavigationPaths> & ColumnMap<TFields, TName>;
152
+ /**
153
+ * Options for fmTableOccurrence function.
154
+ * Provides autocomplete-friendly typing while preserving inference for navigationPaths.
155
+ */
156
+ export interface FMTableOccurrenceOptions<TFields extends Record<string, FieldBuilder<any, any, any, any>>, TName extends string> {
157
+ /** The entity ID (FMTID) for this table occurrence */
158
+ entityId?: `FMTID:${string}`;
159
+ /**
160
+ * Default select behavior:
161
+ * - "all": Select all fields (including related tables)
162
+ * - "schema": Select only schema-defined fields (default)
163
+ * - function: Custom selection from columns
164
+ */
165
+ defaultSelect?: "all" | "schema" | ((columns: ColumnMap<TFields, TName>) => Record<string, Column<any, TName>>);
166
+ /** Navigation paths available from this table (for expand operations) */
167
+ navigationPaths?: readonly string[];
168
+ /** Whether to use entity IDs (FMTID/FMFID) instead of names in queries */
169
+ useEntityIds?: boolean;
170
+ }
171
+ /**
172
+ * Create a table occurrence with field builders.
173
+ * This is the main API for defining tables in the new ORM style.
174
+ *
175
+ * @example
176
+ * const users = fmTableOccurrence("users", {
177
+ * id: textField().primaryKey().entityId("FMFID:1"),
178
+ * name: textField().notNull().entityId("FMFID:6"),
179
+ * active: numberField()
180
+ * .outputValidator(z.coerce.boolean())
181
+ * .inputValidator(z.boolean().transform(v => v ? 1 : 0))
182
+ * .entityId("FMFID:7"),
183
+ * }, {
184
+ * entityId: "FMTID:100",
185
+ * defaultSelect: "schema",
186
+ * navigationPaths: ["contacts"],
187
+ * });
188
+ *
189
+ * // Access columns
190
+ * users.id // Column<string, "id">
191
+ * users.name // Column<string, "name">
192
+ *
193
+ * // Use in queries
194
+ * db.from(users).select("id", "name").where(eq(users.active, true))
195
+ */
196
+ export declare function fmTableOccurrence<const TName extends string, const TFields extends Record<string, FieldBuilder<any, any, any, any>>, const TNavPaths extends readonly string[] = readonly []>(name: TName, fields: TFields, options?: FMTableOccurrenceOptions<TFields, TName> & {
197
+ /** Navigation paths available from this table (for expand operations) */
198
+ navigationPaths?: TNavPaths;
199
+ }): FMTableWithColumns<TFields, TName, TNavPaths>;
200
+ /**
201
+ * Helper to extract the schema type from a TableOccurrence or FMTable.
202
+ */
203
+ export type InferTableSchema<T> = T extends FMTable<infer TFields, any> ? InferSchemaFromFields<TFields> : never;
204
+ /**
205
+ * Extract the schema type from an FMTable instance.
206
+ * This is used to infer the schema from table objects passed to db.from(), expand(), etc.
207
+ */
208
+ export type InferSchemaOutputFromFMTable<T extends FMTable<any, any>> = T extends FMTable<infer TFields, any> ? InferSchemaFromFields<TFields> : never;
209
+ /**
210
+ * Extract the input schema type from an FMTable instance.
211
+ * This is used for insert and update operations where we need write types.
212
+ */
213
+ export type InferInputSchemaFromFMTable<T extends FMTable<any, any>> = T extends FMTable<infer TFields, any> ? InferInputSchemaFromFields<TFields> : never;
214
+ /**
215
+ * Helper type to check if a FieldBuilder's input type excludes null and undefined.
216
+ * This checks the TInput type parameter, which preserves nullability from notNull().
217
+ */
218
+ type FieldInputExcludesNullish<F> = F extends FieldBuilder<any, infer TInput, any> ? null extends TInput ? false : undefined extends TInput ? false : true : false;
219
+ /**
220
+ * Check if a FieldBuilder is readOnly at the type level
221
+ */
222
+ type IsFieldReadOnly<F> = F extends FieldBuilderType<any, any, any, infer ReadOnly> ? ReadOnly extends true ? true : false : false;
223
+ /**
224
+ * Compute insert data type from FMTable, making notNull fields required.
225
+ * Fields are required if their FieldBuilder's TInput type excludes null/undefined.
226
+ * All other fields are optional (can be omitted).
227
+ * readOnly fields are excluded (including primaryKey/idField since they're automatically readOnly).
228
+ */
229
+ export type InsertDataFromFMTable<T extends FMTable<any, any>> = T extends FMTable<infer TFields, any> ? {
230
+ [K in keyof TFields as IsFieldReadOnly<TFields[K]> extends true ? never : FieldInputExcludesNullish<TFields[K]> extends true ? K : never]: InferFieldInput<TFields[K]>;
231
+ } & {
232
+ [K in keyof TFields as IsFieldReadOnly<TFields[K]> extends true ? never : FieldInputExcludesNullish<TFields[K]> extends true ? never : K]?: InferFieldInput<TFields[K]>;
233
+ } : never;
234
+ /**
235
+ * Compute update data type from FMTable.
236
+ * All fields are optional, but readOnly fields are excluded (including primaryKey/idField).
237
+ */
238
+ export type UpdateDataFromFMTable<T extends FMTable<any, any>> = T extends FMTable<infer TFields, any> ? {
239
+ [K in keyof TFields as IsFieldReadOnly<TFields[K]> extends true ? never : K]?: InferFieldInput<TFields[K]>;
240
+ } : never;
241
+ /**
242
+ * Extract the table name type from an FMTable.
243
+ * This is a workaround since we can't directly index Symbols in types.
244
+ */
245
+ export type ExtractTableName<T extends FMTable<any, any>> = T extends FMTable<any, infer Name> ? Name : never;
246
+ /**
247
+ * Validates that a target table's name matches one of the source table's navigationPaths.
248
+ * Used to ensure type-safe expand/navigate operations.
249
+ */
250
+ export type ValidExpandTarget<SourceTable extends FMTable<any, any, any> | undefined, TargetTable extends FMTable<any, any, any>> = SourceTable extends FMTable<any, any, infer SourceNavPaths> ? ExtractTableName<TargetTable> extends SourceNavPaths[number] ? TargetTable : never : TargetTable;
251
+ /**
252
+ * Get the table name from an FMTable instance.
253
+ * @param table - FMTable instance
254
+ * @returns The table name
255
+ */
256
+ export declare function getTableName<T extends FMTable<any, any>>(table: T): string;
257
+ /**
258
+ * Get the entity ID (FMTID) from an FMTable instance.
259
+ * @param table - FMTable instance
260
+ * @returns The entity ID or undefined if not using entity IDs
261
+ */
262
+ export declare function getTableEntityId<T extends FMTable<any, any>>(table: T): string | undefined;
263
+ /**
264
+ * Get the schema validator from an FMTable instance.
265
+ * @param table - FMTable instance
266
+ * @returns The StandardSchemaV1 validator
267
+ */
268
+ export declare function getTableSchema<T extends FMTable<any, any>>(table: T): StandardSchemaV1;
269
+ /**
270
+ * Get the fields from an FMTable instance.
271
+ * @param table - FMTable instance
272
+ * @returns The fields record
273
+ */
274
+ export declare function getTableFields<T extends FMTable<any, any>>(table: T): any;
275
+ /**
276
+ * Get the navigation paths from an FMTable instance.
277
+ * @param table - FMTable instance
278
+ * @returns Array of navigation path names
279
+ */
280
+ export declare function getNavigationPaths<T extends FMTable<any, any>>(table: T): readonly string[];
281
+ /**
282
+ * Get the default select configuration from an FMTable instance.
283
+ * @param table - FMTable instance
284
+ * @returns Default select configuration
285
+ */
286
+ export declare function getDefaultSelect<T extends FMTable<any, any>>(table: T): "all" | "schema" | Record<string, Column<any, any, false>>;
287
+ /**
288
+ * Get the base table configuration from an FMTable instance.
289
+ * This provides access to schema, idField, required fields, readOnly fields, and field IDs.
290
+ * @param table - FMTable instance
291
+ * @returns Base table configuration object
292
+ */
293
+ export declare function getBaseTableConfig<T extends FMTable<any, any>>(table: T): {
294
+ schema: Record<string | number | symbol, StandardSchemaV1<unknown, unknown>>;
295
+ inputSchema?: Record<string | number | symbol, StandardSchemaV1<unknown, unknown>> | undefined;
296
+ idField?: string | number | symbol | undefined;
297
+ required: readonly (string | number | symbol)[];
298
+ readOnly: readonly (string | number | symbol)[];
299
+ containerFields: readonly (string | number | symbol)[];
300
+ fmfIds?: Record<string | number | symbol, `FMFID:${string}`> | undefined;
301
+ };
302
+ /**
303
+ * Check if an FMTable instance is using entity IDs (both FMTID and FMFIDs).
304
+ * @param table - FMTable instance
305
+ * @returns True if using entity IDs, false otherwise
306
+ */
307
+ export declare function isUsingEntityIds<T extends FMTable<any, any>>(table: T): boolean;
308
+ /**
309
+ * Get the field ID (FMFID) for a given field name, or the field name itself if not using IDs.
310
+ * @param table - FMTable instance
311
+ * @param fieldName - Field name to get the ID for
312
+ * @returns The FMFID string or the original field name
313
+ */
314
+ export declare function getFieldId<T extends FMTable<any, any>>(table: T, fieldName: string): string;
315
+ /**
316
+ * Get the field name for a given field ID (FMFID), or the ID itself if not found.
317
+ * @param table - FMTable instance
318
+ * @param fieldId - The FMFID to get the field name for
319
+ * @returns The field name or the original ID
320
+ */
321
+ export declare function getFieldName<T extends FMTable<any, any>>(table: T, fieldId: string): string;
322
+ /**
323
+ * Get the table ID (FMTID or name) from an FMTable instance.
324
+ * Returns the FMTID if available, otherwise returns the table name.
325
+ * @param table - FMTable instance
326
+ * @returns The FMTID string or the table name
327
+ */
328
+ export declare function getTableId<T extends FMTable<any, any>>(table: T): string;
329
+ /**
330
+ * Get all columns from a table as an object.
331
+ * Useful for selecting all fields except some using destructuring.
332
+ *
333
+ * @example
334
+ * const { password, ...cols } = getTableColumns(users)
335
+ * db.from(users).list().select(cols)
336
+ *
337
+ * @param table - FMTable instance
338
+ * @returns Object with all columns from the table
339
+ */
340
+ export declare function getTableColumns<T extends FMTableWithColumns<any, any>>(table: T): ColumnMap<T[typeof FMTableFields], ExtractTableName<T>>;
341
+ export {};
@@ -0,0 +1,211 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ var _a, _b, _c, _d, _e, _f, _g, _h;
5
+ import { Column } from "./column.js";
6
+ import { z } from "zod/v4";
7
+ const FMTableName = Symbol.for("fmodata:FMTableName");
8
+ const FMTableEntityId = Symbol.for("fmodata:FMTableEntityId");
9
+ const FMTableSchema = Symbol.for("fmodata:FMTableSchema");
10
+ const FMTableFields = Symbol.for("fmodata:FMTableFields");
11
+ const FMTableNavigationPaths = Symbol.for("fmodata:FMTableNavigationPaths");
12
+ const FMTableDefaultSelect = Symbol.for("fmodata:FMTableDefaultSelect");
13
+ const FMTableBaseTableConfig = Symbol.for("fmodata:FMTableBaseTableConfig");
14
+ const FMTableUseEntityIds = Symbol.for("fmodata:FMTableUseEntityIds");
15
+ _h = FMTableName, _g = FMTableEntityId, _f = FMTableUseEntityIds, _e = FMTableSchema, _d = FMTableFields, _c = FMTableNavigationPaths, _b = FMTableDefaultSelect, _a = FMTableBaseTableConfig;
16
+ class FMTable {
17
+ constructor(config) {
18
+ /** @internal */
19
+ __publicField(this, _h);
20
+ /** @internal */
21
+ __publicField(this, _g);
22
+ /** @internal */
23
+ __publicField(this, _f);
24
+ /** @internal */
25
+ __publicField(this, _e);
26
+ /** @internal */
27
+ __publicField(this, _d);
28
+ /** @internal */
29
+ __publicField(this, _c);
30
+ /** @internal */
31
+ __publicField(this, _b);
32
+ /** @internal */
33
+ __publicField(this, _a);
34
+ this[FMTableName] = config.name;
35
+ this[FMTableEntityId] = config.entityId;
36
+ this[FMTableUseEntityIds] = config.useEntityIds;
37
+ this[FMTableSchema] = config.schema;
38
+ this[FMTableFields] = config.fields;
39
+ this[FMTableNavigationPaths] = config.navigationPaths;
40
+ this[FMTableDefaultSelect] = config.defaultSelect;
41
+ this[FMTableBaseTableConfig] = config.baseTableConfig;
42
+ }
43
+ }
44
+ /**
45
+ * Internal Symbols for accessing table metadata.
46
+ * @internal - Not intended for public use. Access table properties via columns instead.
47
+ */
48
+ __publicField(FMTable, "Symbol", {
49
+ Name: FMTableName,
50
+ EntityId: FMTableEntityId,
51
+ UseEntityIds: FMTableUseEntityIds,
52
+ Schema: FMTableSchema,
53
+ Fields: FMTableFields,
54
+ NavigationPaths: FMTableNavigationPaths,
55
+ DefaultSelect: FMTableDefaultSelect,
56
+ BaseTableConfig: FMTableBaseTableConfig
57
+ });
58
+ function fmTableOccurrence(name, fields, options) {
59
+ const fieldConfigs = Object.entries(fields).map(([fieldName, builder]) => ({
60
+ fieldName,
61
+ config: builder._getConfig()
62
+ }));
63
+ const primaryKeyField = fieldConfigs.find((f) => f.config.primaryKey);
64
+ const idField = primaryKeyField == null ? void 0 : primaryKeyField.fieldName;
65
+ const required = fieldConfigs.filter((f) => f.config.notNull).map((f) => f.fieldName);
66
+ const readOnly = fieldConfigs.filter((f) => f.config.readOnly).map((f) => f.fieldName);
67
+ const containerFields = fieldConfigs.filter((f) => f.config.fieldType === "container").map((f) => f.fieldName);
68
+ const fmfIds = {};
69
+ for (const { fieldName, config } of fieldConfigs) {
70
+ if (config.entityId) {
71
+ fmfIds[fieldName] = config.entityId;
72
+ }
73
+ }
74
+ const zodSchema = {};
75
+ const inputSchema = {};
76
+ for (const { fieldName, config } of fieldConfigs) {
77
+ if (config.outputValidator) {
78
+ zodSchema[fieldName] = config.outputValidator;
79
+ } else {
80
+ let validator;
81
+ switch (config.fieldType) {
82
+ case "text":
83
+ case "date":
84
+ case "time":
85
+ case "timestamp":
86
+ case "container":
87
+ case "calculated":
88
+ validator = z.string();
89
+ break;
90
+ case "number":
91
+ validator = z.number();
92
+ break;
93
+ default:
94
+ validator = z.unknown();
95
+ }
96
+ if (!config.notNull) {
97
+ validator = validator.nullable();
98
+ }
99
+ zodSchema[fieldName] = validator;
100
+ }
101
+ if (config.inputValidator) {
102
+ inputSchema[fieldName] = config.inputValidator;
103
+ }
104
+ }
105
+ const tableSchema = z.object(zodSchema);
106
+ const baseTableConfig = {
107
+ schema: zodSchema,
108
+ inputSchema: Object.keys(inputSchema).length > 0 ? inputSchema : void 0,
109
+ idField,
110
+ required,
111
+ readOnly,
112
+ containerFields,
113
+ fmfIds: Object.keys(fmfIds).length > 0 ? fmfIds : void 0
114
+ };
115
+ const columns = {};
116
+ for (const [fieldName, builder] of Object.entries(fields)) {
117
+ const config = builder._getConfig();
118
+ columns[fieldName] = new Column({
119
+ fieldName: String(fieldName),
120
+ entityId: config.entityId,
121
+ tableName: name,
122
+ tableEntityId: options == null ? void 0 : options.entityId
123
+ });
124
+ }
125
+ const defaultSelectOption = (options == null ? void 0 : options.defaultSelect) ?? "schema";
126
+ const resolvedDefaultSelect = typeof defaultSelectOption === "function" ? defaultSelectOption(columns) : defaultSelectOption;
127
+ const navigationPaths = (options == null ? void 0 : options.navigationPaths) ?? [];
128
+ const table = new FMTable({
129
+ name,
130
+ entityId: options == null ? void 0 : options.entityId,
131
+ useEntityIds: options == null ? void 0 : options.useEntityIds,
132
+ schema: tableSchema,
133
+ fields,
134
+ navigationPaths,
135
+ defaultSelect: resolvedDefaultSelect,
136
+ baseTableConfig
137
+ });
138
+ Object.assign(table, columns);
139
+ return table;
140
+ }
141
+ function getTableName(table) {
142
+ return table[FMTableName];
143
+ }
144
+ function getNavigationPaths(table) {
145
+ return table[FMTableNavigationPaths];
146
+ }
147
+ function getDefaultSelect(table) {
148
+ return table[FMTableDefaultSelect];
149
+ }
150
+ function getBaseTableConfig(table) {
151
+ return table[FMTableBaseTableConfig];
152
+ }
153
+ function isUsingEntityIds(table) {
154
+ return table[FMTableEntityId] !== void 0 && table[FMTableBaseTableConfig].fmfIds !== void 0;
155
+ }
156
+ function getFieldId(table, fieldName) {
157
+ const config = table[FMTableBaseTableConfig];
158
+ if (config.fmfIds && fieldName in config.fmfIds) {
159
+ const fieldId = config.fmfIds[fieldName];
160
+ if (fieldId) {
161
+ return fieldId;
162
+ }
163
+ }
164
+ return fieldName;
165
+ }
166
+ function getFieldName(table, fieldId) {
167
+ const config = table[FMTableBaseTableConfig];
168
+ if (config.fmfIds) {
169
+ for (const [fieldName, fmfId] of Object.entries(config.fmfIds)) {
170
+ if (fmfId === fieldId) {
171
+ return fieldName;
172
+ }
173
+ }
174
+ }
175
+ return fieldId;
176
+ }
177
+ function getTableId(table) {
178
+ return table[FMTableEntityId] ?? table[FMTableName];
179
+ }
180
+ function getTableColumns(table) {
181
+ var _a2;
182
+ const fields = table[FMTableFields];
183
+ const tableName = table[FMTableName];
184
+ const tableEntityId = table[FMTableEntityId];
185
+ const baseConfig = table[FMTableBaseTableConfig];
186
+ const columns = {};
187
+ for (const [fieldName, builder] of Object.entries(fields)) {
188
+ builder._getConfig();
189
+ columns[fieldName] = new Column({
190
+ fieldName: String(fieldName),
191
+ entityId: (_a2 = baseConfig.fmfIds) == null ? void 0 : _a2[fieldName],
192
+ tableName,
193
+ tableEntityId
194
+ });
195
+ }
196
+ return columns;
197
+ }
198
+ export {
199
+ FMTable,
200
+ fmTableOccurrence,
201
+ getBaseTableConfig,
202
+ getDefaultSelect,
203
+ getFieldId,
204
+ getFieldName,
205
+ getNavigationPaths,
206
+ getTableColumns,
207
+ getTableId,
208
+ getTableName,
209
+ isUsingEntityIds
210
+ };
211
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sources":["../../../src/orm/table.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { FieldBuilder, type ContainerDbType } from \"./field-builders\";\nimport type { FieldBuilder as FieldBuilderType } from \"./field-builders\";\nimport { Column } from \"./column\";\nimport { z } from \"zod/v4\";\n\n/**\n * Extract the output type from a FieldBuilder.\n * This is what you get when reading from the database.\n *\n * This type extracts the TOutput type parameter, which is set by readValidator()\n * and represents the transformed/validated output type.\n */\nexport type InferFieldOutput<F> =\n F extends FieldBuilder<infer TOutput, any, any, any> ? TOutput : never;\n\n/**\n * Extract the input type from a FieldBuilder.\n * This is what you pass when writing to the database.\n *\n * This type extracts the TInput type parameter, which is set by writeValidator()\n * and represents the transformed/validated input type.\n */\ntype InferFieldInput<F> =\n F extends FieldBuilder<any, infer TInput, any, any> ? TInput : never;\n\n/**\n * Build a schema type from field builders (output/read types).\n */\ntype InferSchemaFromFields<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n> = {\n [K in keyof TFields]: InferFieldOutput<TFields[K]>;\n};\n\n/**\n * Build an input schema type from field builders (input/write types).\n * Used for insert and update operations.\n */\ntype InferInputSchemaFromFields<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n> = {\n [K in keyof TFields]: InferFieldInput<TFields[K]>;\n};\n\n/**\n * Check if a field is a container field by inspecting its TDbType.\n * Container fields have a branded TDbType that extends ContainerDbType.\n */\ntype IsContainerField<F> =\n F extends FieldBuilder<any, any, infer TDbType, any>\n ? NonNullable<TDbType> extends ContainerDbType\n ? true\n : false\n : false;\n\n/**\n * Extract only selectable (non-container) field keys from a fields record.\n * Container fields are excluded because they cannot be selected via $select in FileMaker OData.\n */\ntype SelectableFieldKeys<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n> = {\n [K in keyof TFields]: IsContainerField<TFields[K]> extends true ? never : K;\n}[keyof TFields];\n\n/**\n * Build a schema type excluding container fields (for query return types).\n * This is used to ensure container fields don't appear in the return type\n * when using defaultSelect: \"schema\" or \"all\".\n */\ntype InferSelectableSchemaFromFields<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n> = {\n [K in SelectableFieldKeys<TFields>]: InferFieldOutput<TFields[K]>;\n};\n\n/**\n * Internal Symbols for table properties (hidden from IDE autocomplete).\n * These are used to store internal configuration that shouldn't be visible\n * when users access table columns.\n * @internal - Not exported from public API, only accessible via FMTable.Symbol\n */\nconst FMTableName = Symbol.for(\"fmodata:FMTableName\");\nconst FMTableEntityId = Symbol.for(\"fmodata:FMTableEntityId\");\nconst FMTableSchema = Symbol.for(\"fmodata:FMTableSchema\");\nconst FMTableFields = Symbol.for(\"fmodata:FMTableFields\");\nconst FMTableNavigationPaths = Symbol.for(\"fmodata:FMTableNavigationPaths\");\nconst FMTableDefaultSelect = Symbol.for(\"fmodata:FMTableDefaultSelect\");\nconst FMTableBaseTableConfig = Symbol.for(\"fmodata:FMTableBaseTableConfig\");\nconst FMTableUseEntityIds = Symbol.for(\"fmodata:FMTableUseEntityIds\");\n\n/**\n * Base table class with Symbol-based internal properties.\n * This follows the Drizzle ORM pattern where internal configuration\n * is stored via Symbols, keeping it hidden from IDE autocomplete.\n */\nexport class FMTable<\n TFields extends Record<string, FieldBuilder<any, any, any, any>> = any,\n TName extends string = string,\n TNavigationPaths extends readonly string[] = readonly string[],\n> {\n /**\n * Internal Symbols for accessing table metadata.\n * @internal - Not intended for public use. Access table properties via columns instead.\n */\n static readonly Symbol = {\n Name: FMTableName,\n EntityId: FMTableEntityId,\n UseEntityIds: FMTableUseEntityIds,\n Schema: FMTableSchema,\n Fields: FMTableFields,\n NavigationPaths: FMTableNavigationPaths,\n DefaultSelect: FMTableDefaultSelect,\n BaseTableConfig: FMTableBaseTableConfig,\n };\n\n /** @internal */\n [FMTableName]: TName;\n\n /** @internal */\n [FMTableEntityId]?: `FMTID:${string}`;\n\n /** @internal */\n [FMTableUseEntityIds]?: boolean;\n\n /** @internal */\n [FMTableSchema]: StandardSchemaV1<any, InferSchemaFromFields<TFields>>;\n\n /** @internal */\n [FMTableFields]: TFields;\n\n /** @internal */\n [FMTableNavigationPaths]: TNavigationPaths;\n\n /** @internal */\n [FMTableDefaultSelect]: \"all\" | \"schema\" | Record<string, Column<any, TName>>;\n\n /** @internal */\n [FMTableBaseTableConfig]: {\n schema: Record<keyof TFields, StandardSchemaV1>;\n inputSchema?: Record<keyof TFields, StandardSchemaV1>;\n idField?: keyof TFields;\n required: readonly (keyof TFields)[];\n readOnly: readonly (keyof TFields)[];\n containerFields: readonly (keyof TFields)[];\n fmfIds?: Record<keyof TFields, `FMFID:${string}`>;\n };\n\n constructor(config: {\n name: TName;\n entityId?: `FMTID:${string}`;\n useEntityIds?: boolean;\n schema: StandardSchemaV1<any, InferSchemaFromFields<TFields>>;\n fields: TFields;\n navigationPaths: TNavigationPaths;\n defaultSelect: \"all\" | \"schema\" | Record<string, Column<any, TName>>;\n baseTableConfig: {\n schema: Record<keyof TFields, StandardSchemaV1>;\n inputSchema?: Record<keyof TFields, StandardSchemaV1>;\n idField?: keyof TFields;\n required: readonly (keyof TFields)[];\n readOnly: readonly (keyof TFields)[];\n containerFields: readonly (keyof TFields)[];\n fmfIds?: Record<keyof TFields, `FMFID:${string}`>;\n };\n }) {\n this[FMTableName] = config.name;\n this[FMTableEntityId] = config.entityId;\n this[FMTableUseEntityIds] = config.useEntityIds;\n this[FMTableSchema] = config.schema;\n this[FMTableFields] = config.fields;\n this[FMTableNavigationPaths] = config.navigationPaths;\n this[FMTableDefaultSelect] = config.defaultSelect;\n this[FMTableBaseTableConfig] = config.baseTableConfig;\n }\n}\n\n/**\n * Type helper to extract the column map from fields.\n * Table name is baked into each column type for validation.\n * Container fields are marked with IsContainer=true.\n */\nexport type ColumnMap<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n TName extends string,\n> = {\n [K in keyof TFields]: Column<\n InferFieldOutput<TFields[K]>,\n TName,\n IsContainerField<TFields[K]>\n >;\n};\n\n/**\n * Extract only selectable (non-container) columns from a table.\n * This is used to prevent selecting container fields in queries.\n */\nexport type SelectableColumnMap<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n TName extends string,\n> = {\n [K in SelectableFieldKeys<TFields>]: Column<\n InferFieldOutput<TFields[K]>,\n TName,\n false\n >;\n};\n\n/**\n * Validates that a select object doesn't contain container field columns.\n * Returns never if any container fields are found, otherwise returns the original type.\n */\nexport type ValidateNoContainerFields<\n TSelect extends Record<string, Column<any, any, any>>,\n> = {\n [K in keyof TSelect]: TSelect[K] extends Column<any, any, true>\n ? never\n : TSelect[K];\n} extends TSelect\n ? TSelect\n : {\n [K in keyof TSelect]: TSelect[K] extends Column<any, any, true>\n ? \"❌ Container fields cannot be selected. Use .getSingleField() instead.\"\n : TSelect[K];\n };\n\n/**\n * Extract the keys from a defaultSelect function's return type.\n * Used to infer which fields are selected by default for type narrowing.\n */\ntype ExtractDefaultSelectKeys<\n TDefaultSelect,\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n TName extends string,\n> = TDefaultSelect extends (columns: ColumnMap<TFields, TName>) => infer R\n ? keyof R\n : TDefaultSelect extends \"schema\"\n ? keyof TFields\n : keyof TFields; // \"all\" defaults to all keys\n\n/**\n * Complete table type with both metadata (via Symbols) and column accessors.\n * This is the return type of fmTableOccurrence - users see columns directly,\n * but internal config is hidden via Symbols.\n */\nexport type FMTableWithColumns<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n TName extends string,\n TNavigationPaths extends readonly string[] = readonly string[],\n> = FMTable<TFields, TName, TNavigationPaths> & ColumnMap<TFields, TName>;\n\n/**\n * Options for fmTableOccurrence function.\n * Provides autocomplete-friendly typing while preserving inference for navigationPaths.\n */\nexport interface FMTableOccurrenceOptions<\n TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n TName extends string,\n> {\n /** The entity ID (FMTID) for this table occurrence */\n entityId?: `FMTID:${string}`;\n\n /**\n * Default select behavior:\n * - \"all\": Select all fields (including related tables)\n * - \"schema\": Select only schema-defined fields (default)\n * - function: Custom selection from columns\n */\n defaultSelect?:\n | \"all\"\n | \"schema\"\n | ((\n columns: ColumnMap<TFields, TName>,\n ) => Record<string, Column<any, TName>>);\n\n /** Navigation paths available from this table (for expand operations) */\n navigationPaths?: readonly string[];\n\n /** Whether to use entity IDs (FMTID/FMFID) instead of names in queries */\n useEntityIds?: boolean;\n}\n\n/**\n * Create a table occurrence with field builders.\n * This is the main API for defining tables in the new ORM style.\n *\n * @example\n * const users = fmTableOccurrence(\"users\", {\n * id: textField().primaryKey().entityId(\"FMFID:1\"),\n * name: textField().notNull().entityId(\"FMFID:6\"),\n * active: numberField()\n * .outputValidator(z.coerce.boolean())\n * .inputValidator(z.boolean().transform(v => v ? 1 : 0))\n * .entityId(\"FMFID:7\"),\n * }, {\n * entityId: \"FMTID:100\",\n * defaultSelect: \"schema\",\n * navigationPaths: [\"contacts\"],\n * });\n *\n * // Access columns\n * users.id // Column<string, \"id\">\n * users.name // Column<string, \"name\">\n *\n * // Use in queries\n * db.from(users).select(\"id\", \"name\").where(eq(users.active, true))\n */\nexport function fmTableOccurrence<\n const TName extends string,\n const TFields extends Record<string, FieldBuilder<any, any, any, any>>,\n const TNavPaths extends readonly string[] = readonly [],\n>(\n name: TName,\n fields: TFields,\n options?: FMTableOccurrenceOptions<TFields, TName> & {\n /** Navigation paths available from this table (for expand operations) */\n navigationPaths?: TNavPaths;\n },\n): FMTableWithColumns<TFields, TName, TNavPaths> {\n // Extract configuration from field builders\n const fieldConfigs = Object.entries(fields).map(([fieldName, builder]) => ({\n fieldName,\n config: (builder as any)._getConfig(),\n }));\n\n // Find primary key field\n const primaryKeyField = fieldConfigs.find((f) => f.config.primaryKey);\n const idField = primaryKeyField?.fieldName;\n\n // Collect required fields (notNull fields)\n const required = fieldConfigs\n .filter((f) => f.config.notNull)\n .map((f) => f.fieldName);\n\n // Collect read-only fields\n const readOnly = fieldConfigs\n .filter((f) => f.config.readOnly)\n .map((f) => f.fieldName);\n\n // Collect container fields (cannot be selected via $select)\n const containerFields = fieldConfigs\n .filter((f) => f.config.fieldType === \"container\")\n .map((f) => f.fieldName);\n\n // Collect entity IDs\n const fmfIds: Record<string, `FMFID:${string}`> = {};\n for (const { fieldName, config } of fieldConfigs) {\n if (config.entityId) {\n fmfIds[fieldName] = config.entityId;\n }\n }\n\n // Build Zod schema from field builders (output/read validators)\n const zodSchema: Record<string, StandardSchemaV1> = {};\n // Build input schema from field builders (input/write validators)\n const inputSchema: Record<string, StandardSchemaV1> = {};\n\n for (const { fieldName, config } of fieldConfigs) {\n // Use outputValidator if provided, otherwise create a basic validator\n if (config.outputValidator) {\n zodSchema[fieldName] = config.outputValidator;\n } else {\n // Create a default validator based on field type and nullability\n let validator: any;\n switch (config.fieldType) {\n case \"text\":\n case \"date\":\n case \"time\":\n case \"timestamp\":\n case \"container\":\n case \"calculated\":\n validator = z.string();\n break;\n case \"number\":\n validator = z.number();\n break;\n default:\n validator = z.unknown();\n }\n\n // Add nullability if not marked as notNull\n if (!config.notNull) {\n validator = validator.nullable();\n }\n\n zodSchema[fieldName] = validator;\n }\n\n // Store inputValidator if provided (for write operations)\n if (config.inputValidator) {\n inputSchema[fieldName] = config.inputValidator;\n }\n }\n\n // Create a schema validator for the entire table\n const tableSchema = z.object(zodSchema) as unknown as StandardSchemaV1<\n any,\n InferSchemaFromFields<TFields>\n >;\n\n // Build BaseTable-compatible config\n const baseTableConfig = {\n schema: zodSchema as Record<keyof TFields, StandardSchemaV1>,\n inputSchema: (Object.keys(inputSchema).length > 0\n ? inputSchema\n : undefined) as Record<keyof TFields, StandardSchemaV1> | undefined,\n idField: idField as keyof TFields | undefined,\n required: required as readonly (keyof TFields)[],\n readOnly: readOnly as readonly (keyof TFields)[],\n containerFields: containerFields as readonly (keyof TFields)[],\n fmfIds: (Object.keys(fmfIds).length > 0 ? fmfIds : undefined) as\n | Record<keyof TFields, `FMFID:${string}`>\n | undefined,\n };\n\n // Create column instances\n const columns: Record<string, Column> = {};\n for (const [fieldName, builder] of Object.entries(fields)) {\n const config = (builder as any)._getConfig();\n columns[fieldName] = new Column({\n fieldName: String(fieldName),\n entityId: config.entityId,\n tableName: name,\n tableEntityId: options?.entityId,\n });\n }\n\n // Resolve defaultSelect: if it's a function, call it with columns; otherwise use as-is\n const defaultSelectOption = options?.defaultSelect ?? \"schema\";\n const resolvedDefaultSelect:\n | \"all\"\n | \"schema\"\n | Record<string, Column<any, TName>> =\n typeof defaultSelectOption === \"function\"\n ? defaultSelectOption(columns as ColumnMap<TFields, TName>)\n : defaultSelectOption;\n\n // Create the FMTable instance with Symbol-based internal properties\n const navigationPaths = (options?.navigationPaths ?? []) as TNavPaths;\n const table = new FMTable<TFields, TName, TNavPaths>({\n name,\n entityId: options?.entityId,\n useEntityIds: options?.useEntityIds,\n schema: tableSchema,\n fields,\n navigationPaths,\n defaultSelect: resolvedDefaultSelect,\n baseTableConfig,\n });\n\n // Assign columns to the table instance (making them accessible directly)\n Object.assign(table, columns);\n\n return table as FMTableWithColumns<TFields, TName, TNavPaths>;\n}\n\n// /**\n// * Type guard to check if a value is a TableOccurrence or FMTable.\n// * Supports both Symbol-based (new) and underscore-prefixed (legacy) formats.\n// */\n// function isTableOccurrence(value: any): value is TableOccurrence {\n// if (!value || typeof value !== \"object\") {\n// return false;\n// }\n\n// // Check for Symbol-based format (new FMTable class)\n// if (\n// FMTableName in value &&\n// FMTableSchema in value &&\n// FMTableFields in value\n// ) {\n// return typeof value[FMTableName] === \"string\";\n// }\n\n// // Check for underscore-prefixed format (legacy interface)\n// if (\"_name\" in value && \"_schema\" in value && \"_fields\" in value) {\n// return typeof value._name === \"string\";\n// }\n\n// return false;\n// }\n\n/**\n * Helper to extract the schema type from a TableOccurrence or FMTable.\n */\nexport type InferTableSchema<T> =\n T extends FMTable<infer TFields, any>\n ? InferSchemaFromFields<TFields>\n : never;\n\n/**\n * Extract the schema type from an FMTable instance.\n * This is used to infer the schema from table objects passed to db.from(), expand(), etc.\n */\nexport type InferSchemaOutputFromFMTable<T extends FMTable<any, any>> =\n T extends FMTable<infer TFields, any>\n ? InferSchemaFromFields<TFields>\n : never;\n\n/**\n * Extract the input schema type from an FMTable instance.\n * This is used for insert and update operations where we need write types.\n */\nexport type InferInputSchemaFromFMTable<T extends FMTable<any, any>> =\n T extends FMTable<infer TFields, any>\n ? InferInputSchemaFromFields<TFields>\n : never;\n\n/**\n * Helper type to check if a FieldBuilder's input type excludes null and undefined.\n * This checks the TInput type parameter, which preserves nullability from notNull().\n */\ntype FieldInputExcludesNullish<F> =\n F extends FieldBuilder<any, infer TInput, any>\n ? null extends TInput\n ? false\n : undefined extends TInput\n ? false\n : true\n : false;\n\n/**\n * Check if a FieldBuilder is readOnly at the type level\n */\ntype IsFieldReadOnly<F> =\n F extends FieldBuilderType<any, any, any, infer ReadOnly>\n ? ReadOnly extends true\n ? true\n : false\n : false;\n\n/**\n * Compute insert data type from FMTable, making notNull fields required.\n * Fields are required if their FieldBuilder's TInput type excludes null/undefined.\n * All other fields are optional (can be omitted).\n * readOnly fields are excluded (including primaryKey/idField since they're automatically readOnly).\n */\nexport type InsertDataFromFMTable<T extends FMTable<any, any>> =\n T extends FMTable<infer TFields, any>\n ? {\n [K in keyof TFields as IsFieldReadOnly<TFields[K]> extends true\n ? never\n : FieldInputExcludesNullish<TFields[K]> extends true\n ? K\n : never]: InferFieldInput<TFields[K]>;\n } & {\n [K in keyof TFields as IsFieldReadOnly<TFields[K]> extends true\n ? never\n : FieldInputExcludesNullish<TFields[K]> extends true\n ? never\n : K]?: InferFieldInput<TFields[K]>;\n }\n : never;\n\n/**\n * Compute update data type from FMTable.\n * All fields are optional, but readOnly fields are excluded (including primaryKey/idField).\n */\nexport type UpdateDataFromFMTable<T extends FMTable<any, any>> =\n T extends FMTable<infer TFields, any>\n ? {\n [K in keyof TFields as IsFieldReadOnly<TFields[K]> extends true\n ? never\n : K]?: InferFieldInput<TFields[K]>;\n }\n : never;\n\n/**\n * Extract the table name type from an FMTable.\n * This is a workaround since we can't directly index Symbols in types.\n */\nexport type ExtractTableName<T extends FMTable<any, any>> =\n T extends FMTable<any, infer Name> ? Name : never;\n\n/**\n * Validates that a target table's name matches one of the source table's navigationPaths.\n * Used to ensure type-safe expand/navigate operations.\n */\nexport type ValidExpandTarget<\n SourceTable extends FMTable<any, any, any> | undefined,\n TargetTable extends FMTable<any, any, any>,\n> =\n SourceTable extends FMTable<any, any, infer SourceNavPaths>\n ? ExtractTableName<TargetTable> extends SourceNavPaths[number]\n ? TargetTable\n : never\n : TargetTable;\n\n// ============================================================================\n// Helper Functions for Accessing FMTable Internal Properties\n// ============================================================================\n\n/**\n * Get the table name from an FMTable instance.\n * @param table - FMTable instance\n * @returns The table name\n */\nexport function getTableName<T extends FMTable<any, any>>(table: T): string {\n return table[FMTableName];\n}\n\n/**\n * Get the entity ID (FMTID) from an FMTable instance.\n * @param table - FMTable instance\n * @returns The entity ID or undefined if not using entity IDs\n */\nexport function getTableEntityId<T extends FMTable<any, any>>(\n table: T,\n): string | undefined {\n return table[FMTableEntityId];\n}\n\n/**\n * Get the schema validator from an FMTable instance.\n * @param table - FMTable instance\n * @returns The StandardSchemaV1 validator\n */\nexport function getTableSchema<T extends FMTable<any, any>>(\n table: T,\n): StandardSchemaV1 {\n return table[FMTableSchema];\n}\n\n/**\n * Get the fields from an FMTable instance.\n * @param table - FMTable instance\n * @returns The fields record\n */\nexport function getTableFields<T extends FMTable<any, any>>(table: T) {\n return table[FMTableFields];\n}\n\n/**\n * Get the navigation paths from an FMTable instance.\n * @param table - FMTable instance\n * @returns Array of navigation path names\n */\nexport function getNavigationPaths<T extends FMTable<any, any>>(\n table: T,\n): readonly string[] {\n return table[FMTableNavigationPaths];\n}\n\n/**\n * Get the default select configuration from an FMTable instance.\n * @param table - FMTable instance\n * @returns Default select configuration\n */\nexport function getDefaultSelect<T extends FMTable<any, any>>(table: T) {\n return table[FMTableDefaultSelect];\n}\n\n/**\n * Get the base table configuration from an FMTable instance.\n * This provides access to schema, idField, required fields, readOnly fields, and field IDs.\n * @param table - FMTable instance\n * @returns Base table configuration object\n */\nexport function getBaseTableConfig<T extends FMTable<any, any>>(table: T) {\n return table[FMTableBaseTableConfig];\n}\n\n/**\n * Check if an FMTable instance is using entity IDs (both FMTID and FMFIDs).\n * @param table - FMTable instance\n * @returns True if using entity IDs, false otherwise\n */\nexport function isUsingEntityIds<T extends FMTable<any, any>>(\n table: T,\n): boolean {\n return (\n table[FMTableEntityId] !== undefined &&\n table[FMTableBaseTableConfig].fmfIds !== undefined\n );\n}\n\n/**\n * Get the field ID (FMFID) for a given field name, or the field name itself if not using IDs.\n * @param table - FMTable instance\n * @param fieldName - Field name to get the ID for\n * @returns The FMFID string or the original field name\n */\nexport function getFieldId<T extends FMTable<any, any>>(\n table: T,\n fieldName: string,\n): string {\n const config = table[FMTableBaseTableConfig];\n if (config.fmfIds && fieldName in config.fmfIds) {\n const fieldId = config.fmfIds[fieldName];\n if (fieldId) {\n return fieldId;\n }\n }\n return fieldName;\n}\n\n/**\n * Get the field name for a given field ID (FMFID), or the ID itself if not found.\n * @param table - FMTable instance\n * @param fieldId - The FMFID to get the field name for\n * @returns The field name or the original ID\n */\nexport function getFieldName<T extends FMTable<any, any>>(\n table: T,\n fieldId: string,\n): string {\n const config = table[FMTableBaseTableConfig];\n if (config.fmfIds) {\n for (const [fieldName, fmfId] of Object.entries(config.fmfIds)) {\n if (fmfId === fieldId) {\n return fieldName;\n }\n }\n }\n return fieldId;\n}\n/**\n * Get the table ID (FMTID or name) from an FMTable instance.\n * Returns the FMTID if available, otherwise returns the table name.\n * @param table - FMTable instance\n * @returns The FMTID string or the table name\n */\nexport function getTableId<T extends FMTable<any, any>>(table: T): string {\n return table[FMTableEntityId] ?? table[FMTableName];\n}\n\n/**\n * Get all columns from a table as an object.\n * Useful for selecting all fields except some using destructuring.\n *\n * @example\n * const { password, ...cols } = getTableColumns(users)\n * db.from(users).list().select(cols)\n *\n * @param table - FMTable instance\n * @returns Object with all columns from the table\n */\nexport function getTableColumns<T extends FMTableWithColumns<any, any>>(\n table: T,\n): ColumnMap<T[typeof FMTableFields], ExtractTableName<T>> {\n const fields = table[FMTableFields];\n const tableName = table[FMTableName];\n const tableEntityId = table[FMTableEntityId];\n const baseConfig = table[FMTableBaseTableConfig];\n\n const columns: Record<string, Column> = {};\n for (const [fieldName, builder] of Object.entries(fields)) {\n const config = (builder as any)._getConfig();\n columns[fieldName] = new Column({\n fieldName: String(fieldName),\n entityId: baseConfig.fmfIds?.[fieldName],\n tableName: tableName,\n tableEntityId: tableEntityId,\n });\n }\n\n return columns as ColumnMap<T[typeof FMTableFields], ExtractTableName<T>>;\n}\n"],"names":["_a"],"mappings":"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"}