@proofkit/fmodata 0.1.0-alpha.15 → 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 (44) hide show
  1. package/README.md +4 -165
  2. package/dist/esm/client/builders/select-mixin.d.ts +2 -2
  3. package/dist/esm/client/builders/select-mixin.js.map +1 -1
  4. package/dist/esm/client/entity-set.d.ts +2 -12
  5. package/dist/esm/client/entity-set.js.map +1 -1
  6. package/dist/esm/client/error-parser.js.map +1 -1
  7. package/dist/esm/client/query/query-builder.d.ts +9 -16
  8. package/dist/esm/client/query/query-builder.js +7 -76
  9. package/dist/esm/client/query/query-builder.js.map +1 -1
  10. package/dist/esm/client/query/types.d.ts +5 -5
  11. package/dist/esm/client/record-builder.d.ts +5 -5
  12. package/dist/esm/client/record-builder.js.map +1 -1
  13. package/dist/esm/index.d.ts +2 -2
  14. package/dist/esm/orm/column.d.ts +21 -4
  15. package/dist/esm/orm/column.js +5 -2
  16. package/dist/esm/orm/column.js.map +1 -1
  17. package/dist/esm/orm/field-builders.d.ts +1 -1
  18. package/dist/esm/orm/field-builders.js +1 -1
  19. package/dist/esm/orm/field-builders.js.map +1 -1
  20. package/dist/esm/orm/operators.d.ts +19 -19
  21. package/dist/esm/orm/operators.js +31 -12
  22. package/dist/esm/orm/operators.js.map +1 -1
  23. package/dist/esm/orm/table.d.ts +10 -9
  24. package/dist/esm/orm/table.js +5 -3
  25. package/dist/esm/orm/table.js.map +1 -1
  26. package/dist/esm/transform.js +1 -5
  27. package/dist/esm/transform.js.map +1 -1
  28. package/dist/esm/types.d.ts +9 -43
  29. package/dist/esm/types.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/client/builders/select-mixin.ts +2 -3
  32. package/src/client/entity-set.ts +25 -7
  33. package/src/client/error-parser.ts +3 -0
  34. package/src/client/query/query-builder.ts +14 -123
  35. package/src/client/query/types.ts +6 -5
  36. package/src/client/record-builder.ts +9 -6
  37. package/src/index.ts +3 -15
  38. package/src/orm/column.ts +33 -5
  39. package/src/orm/field-builders.ts +1 -1
  40. package/src/orm/operators.ts +105 -51
  41. package/src/orm/table.ts +21 -13
  42. package/src/types.ts +10 -51
  43. package/dist/esm/filter-types.d.ts +0 -76
  44. package/src/filter-types.ts +0 -97
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","sources":["../../src/transform.ts"],"sourcesContent":["import type { FMTable } from \"./orm/table\";\nimport {\n getBaseTableConfig,\n getFieldId,\n getFieldName,\n getTableId,\n getTableName,\n isUsingEntityIds,\n} from \"./orm/table\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Transforms field names to FileMaker field IDs (FMFID) in an object\n * @param data - Object with field names as keys\n * @param table - FMTable instance to get field IDs from\n * @returns Object with FMFID keys instead of field names\n */\nexport function transformFieldNamesToIds<T extends Record<string, any>>(\n data: T,\n table: FMTable<any, any>,\n): Record<string, any> {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [fieldName, value] of Object.entries(data)) {\n const fieldId = getFieldId(table, fieldName);\n transformed[fieldId] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms FileMaker field IDs (FMFID) to field names in an object\n * @param data - Object with FMFID keys\n * @param table - FMTable instance to get field names from\n * @returns Object with field names as keys instead of FMFIDs\n */\nexport function transformFieldIdsToNames<T extends Record<string, any>>(\n data: T,\n table: FMTable<any, any>,\n): Record<string, any> {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this is an OData metadata field (starts with @)\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n const fieldName = getFieldName(table, key);\n transformed[fieldName] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms a field name to FMFID or returns the field name if not using IDs\n * @param fieldName - The field name to transform\n * @param table - FMTable instance to get field ID from\n * @returns The FMFID or field name\n */\nexport function transformFieldName(\n fieldName: string,\n table: FMTable<any, any>,\n): string {\n return getFieldId(table, fieldName);\n}\n\n/**\n * Transforms a table name to FMTID or returns the name if not using IDs\n * @param table - FMTable instance to get table ID from\n * @returns The FMTID or table name\n */\nexport function transformTableName(table: FMTable<any, any>): string {\n return getTableId(table);\n}\n\n/**\n * Gets both table name and ID from a table\n * @param table - FMTable instance\n * @returns Object with name (always present) and id (may be undefined if not using IDs)\n */\nexport function getTableIdentifiers(\n table: FMTable<any, any>,\n): { name: string; id: string | undefined } {\n return {\n name: getTableName(table),\n id: isUsingEntityIds(table) ? getTableId(table) : undefined,\n };\n}\n\n/**\n * Transforms response data by converting field IDs back to field names recursively.\n * Handles both single records and arrays of records, as well as nested expand relationships.\n *\n * @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)\n * @param table - FMTable instance for the main table\n * @param expandConfigs - Configuration for expanded relations (optional)\n * @returns Transformed data with field names instead of IDs\n */\nexport function transformResponseFields(\n data: any,\n table: FMTable<any, any>,\n expandConfigs?: Array<{\n relation: string;\n table?: FMTable<any, any>;\n }>,\n): any {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return data;\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle OData list response with value array\n if (data.value && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((record: any) =>\n transformSingleRecord(record, table, expandConfigs),\n ),\n };\n }\n\n // Handle array of records\n if (Array.isArray(data)) {\n return data.map((record) =>\n transformSingleRecord(record, table, expandConfigs),\n );\n }\n\n // Handle single record\n return transformSingleRecord(data, table, expandConfigs);\n}\n\n/**\n * Transforms a single record, converting field IDs to names and handling nested expands\n */\nfunction transformSingleRecord(\n record: any,\n table: FMTable<any, any>,\n expandConfigs?: Array<{\n relation: string;\n table?: FMTable<any, any>;\n }>,\n): any {\n if (!record || typeof record !== \"object\") {\n return record;\n }\n\n const transformed: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(record)) {\n // Preserve OData metadata fields\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n // Check if this is an expanded relation (by relation name)\n let expandConfig = expandConfigs?.find((ec) => ec.relation === key);\n\n // If not found by relation name, check if this key is a FMTID\n // (FileMaker returns expanded relations with FMTID keys when using entity IDs)\n if (!expandConfig && key.startsWith(\"FMTID:\")) {\n expandConfig = expandConfigs?.find(\n (ec) =>\n ec.table && isUsingEntityIds(ec.table) && getTableId(ec.table) === key,\n );\n }\n\n if (expandConfig && expandConfig.table) {\n // Transform the expanded relation data recursively\n // Use the relation name (not the FMTID) as the key\n const relationKey = expandConfig.relation;\n\n if (Array.isArray(value)) {\n transformed[relationKey] = value.map((nestedRecord) =>\n transformSingleRecord(\n nestedRecord,\n expandConfig.table!,\n undefined, // Don't pass nested expand configs for now\n ),\n );\n } else if (value && typeof value === \"object\") {\n transformed[relationKey] = transformSingleRecord(\n value,\n expandConfig.table,\n undefined,\n );\n } else {\n transformed[relationKey] = value;\n }\n continue;\n }\n\n // Transform field ID to field name\n const fieldName = getFieldName(table, key);\n transformed[fieldName] = value;\n }\n\n return transformed;\n}\n\n/**\n * Transforms an array of field names to FMFIDs\n * @param fieldNames - Array of field names\n * @param table - FMTable instance to get field IDs from\n * @returns Array of FMFIDs or field names\n */\nexport function transformFieldNamesArray(\n fieldNames: string[],\n table: FMTable<any, any>,\n): string[] {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return fieldNames;\n }\n\n return fieldNames.map((fieldName) => getFieldId(table, fieldName));\n}\n\n/**\n * Transforms a field name in an orderBy string (e.g., \"name desc\" -> \"FMFID:1 desc\")\n * @param orderByString - The orderBy string (field name with optional asc/desc)\n * @param table - FMTable instance to get field ID from\n * @returns Transformed orderBy string with FMFID\n */\nexport function transformOrderByField(\n orderByString: string,\n table: FMTable<any, any> | undefined,\n): string {\n if (!table) {\n return orderByString;\n }\n const config = getBaseTableConfig(table);\n if (!config || !config.fmfIds) {\n return orderByString;\n }\n\n // Parse the orderBy string to extract field name and direction\n const parts = orderByString.trim().split(/\\s+/);\n const fieldName = parts[0];\n if (!fieldName) {\n return orderByString;\n }\n const direction = parts[1]; // \"asc\" or \"desc\" or undefined\n\n const fieldId = getFieldId(table, fieldName);\n return direction ? `${fieldId} ${direction}` : fieldId;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transform.js","sources":["../../src/transform.ts"],"sourcesContent":["import type { FMTable } from \"./orm/table\";\nimport {\n getBaseTableConfig,\n getFieldId,\n getFieldName,\n getTableId,\n getTableName,\n isUsingEntityIds,\n} from \"./orm/table\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Transforms field names to FileMaker field IDs (FMFID) in an object\n * @param data - Object with field names as keys\n * @param table - FMTable instance to get field IDs from\n * @returns Object with FMFID keys instead of field names\n */\nexport function transformFieldNamesToIds<T extends Record<string, any>>(\n data: T,\n table: FMTable<any, any>,\n): Record<string, any> {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [fieldName, value] of Object.entries(data)) {\n const fieldId = getFieldId(table, fieldName);\n transformed[fieldId] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms FileMaker field IDs (FMFID) to field names in an object\n * @param data - Object with FMFID keys\n * @param table - FMTable instance to get field names from\n * @returns Object with field names as keys instead of FMFIDs\n */\nexport function transformFieldIdsToNames<T extends Record<string, any>>(\n data: T,\n table: FMTable<any, any>,\n): Record<string, any> {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this is an OData metadata field (starts with @)\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n const fieldName = getFieldName(table, key);\n transformed[fieldName] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms a field name to FMFID or returns the field name if not using IDs\n * @param fieldName - The field name to transform\n * @param table - FMTable instance to get field ID from\n * @returns The FMFID or field name\n */\nexport function transformFieldName(\n fieldName: string,\n table: FMTable<any, any>,\n): string {\n return getFieldId(table, fieldName);\n}\n\n/**\n * Transforms a table name to FMTID or returns the name if not using IDs\n * @param table - FMTable instance to get table ID from\n * @returns The FMTID or table name\n */\nexport function transformTableName(table: FMTable<any, any>): string {\n return getTableId(table);\n}\n\n/**\n * Gets both table name and ID from a table\n * @param table - FMTable instance\n * @returns Object with name (always present) and id (may be undefined if not using IDs)\n */\nexport function getTableIdentifiers(\n table: FMTable<any, any>,\n): { name: string; id: string | undefined } {\n return {\n name: getTableName(table),\n id: isUsingEntityIds(table) ? getTableId(table) : undefined,\n };\n}\n\n/**\n * Transforms response data by converting field IDs back to field names recursively.\n * Handles both single records and arrays of records, as well as nested expand relationships.\n *\n * @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)\n * @param table - FMTable instance for the main table\n * @param expandConfigs - Configuration for expanded relations (optional)\n * @returns Transformed data with field names instead of IDs\n */\nexport function transformResponseFields(\n data: any,\n table: FMTable<any, any>,\n expandConfigs?: Array<{\n relation: string;\n table?: FMTable<any, any>;\n }>,\n): any {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return data;\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle OData list response with value array\n if (data.value && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((record: any) =>\n transformSingleRecord(record, table, expandConfigs),\n ),\n };\n }\n\n // Handle array of records\n if (Array.isArray(data)) {\n return data.map((record) =>\n transformSingleRecord(record, table, expandConfigs),\n );\n }\n\n // Handle single record\n return transformSingleRecord(data, table, expandConfigs);\n}\n\n/**\n * Transforms a single record, converting field IDs to names and handling nested expands\n */\nfunction transformSingleRecord(\n record: any,\n table: FMTable<any, any>,\n expandConfigs?: Array<{\n relation: string;\n table?: FMTable<any, any>;\n }>,\n): any {\n if (!record || typeof record !== \"object\") {\n return record;\n }\n\n const transformed: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(record)) {\n // Preserve OData metadata fields\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n // Check if this is an expanded relation (by relation name)\n let expandConfig = expandConfigs?.find((ec) => ec.relation === key);\n\n // If not found by relation name, check if this key is a FMTID\n // (FileMaker returns expanded relations with FMTID keys when using entity IDs)\n if (!expandConfig && key.startsWith(\"FMTID:\")) {\n expandConfig = expandConfigs?.find(\n (ec) =>\n ec.table && isUsingEntityIds(ec.table) && getTableId(ec.table) === key,\n );\n }\n\n if (expandConfig && expandConfig.table) {\n // Transform the expanded relation data recursively\n // Use the relation name (not the FMTID) as the key\n const relationKey = expandConfig.relation;\n\n if (Array.isArray(value)) {\n transformed[relationKey] = value.map((nestedRecord) =>\n transformSingleRecord(\n nestedRecord,\n expandConfig.table!,\n undefined, // Don't pass nested expand configs for now\n ),\n );\n } else if (value && typeof value === \"object\") {\n transformed[relationKey] = transformSingleRecord(\n value,\n expandConfig.table,\n undefined,\n );\n } else {\n transformed[relationKey] = value;\n }\n continue;\n }\n\n // Transform field ID to field name\n const fieldName = getFieldName(table, key);\n transformed[fieldName] = value;\n }\n\n return transformed;\n}\n\n/**\n * Transforms an array of field names to FMFIDs\n * @param fieldNames - Array of field names\n * @param table - FMTable instance to get field IDs from\n * @returns Array of FMFIDs or field names\n */\nexport function transformFieldNamesArray(\n fieldNames: string[],\n table: FMTable<any, any>,\n): string[] {\n const config = getBaseTableConfig(table);\n if (!config.fmfIds) {\n return fieldNames;\n }\n\n return fieldNames.map((fieldName) => getFieldId(table, fieldName));\n}\n\n/**\n * Transforms a field name in an orderBy string (e.g., \"name desc\" -> \"FMFID:1 desc\")\n * @param orderByString - The orderBy string (field name with optional asc/desc)\n * @param table - FMTable instance to get field ID from\n * @returns Transformed orderBy string with FMFID\n */\nexport function transformOrderByField(\n orderByString: string,\n table: FMTable<any, any> | undefined,\n): string {\n if (!table) {\n return orderByString;\n }\n const config = getBaseTableConfig(table);\n if (!config || !config.fmfIds) {\n return orderByString;\n }\n\n // Parse the orderBy string to extract field name and direction\n const parts = orderByString.trim().split(/\\s+/);\n const fieldName = parts[0];\n if (!fieldName) {\n return orderByString;\n }\n const direction = parts[1]; // \"asc\" or \"desc\" or undefined\n\n const fieldId = getFieldId(table, fieldName);\n return direction ? `${fieldId} ${direction}` : fieldId;\n}\n"],"names":[],"mappings":";AAiBgB,SAAA,yBACd,MACA,OACqB;AACf,QAAA,SAAS,mBAAmB,KAAK;AACnC,MAAA,CAAC,OAAO,QAAQ;AACX,WAAA;AAAA,EAAA;AAGT,QAAM,cAAmC,CAAC;AAC1C,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAA,UAAU,WAAW,OAAO,SAAS;AAC3C,gBAAY,OAAO,IAAI;AAAA,EAAA;AAElB,SAAA;AACT;AA4EgB,SAAA,wBACd,MACA,OACA,eAIK;AACC,QAAA,SAAS,mBAAmB,KAAK;AACnC,MAAA,CAAC,OAAO,QAAQ;AACX,WAAA;AAAA,EAAA;AAIL,MAAA,SAAS,QAAQ,SAAS,QAAW;AAChC,WAAA;AAAA,EAAA;AAIT,MAAI,KAAK,SAAS,MAAM,QAAQ,KAAK,KAAK,GAAG;AACpC,WAAA;AAAA,MACL,GAAG;AAAA,MACH,OAAO,KAAK,MAAM;AAAA,QAAI,CAAC,WACrB,sBAAsB,QAAQ,OAAO,aAAa;AAAA,MAAA;AAAA,IAEtD;AAAA,EAAA;AAIE,MAAA,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK;AAAA,MAAI,CAAC,WACf,sBAAsB,QAAQ,OAAO,aAAa;AAAA,IACpD;AAAA,EAAA;AAIK,SAAA,sBAAsB,MAAM,OAAO,aAAa;AACzD;AAKA,SAAS,sBACP,QACA,OACA,eAIK;AACL,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AAClC,WAAA;AAAA,EAAA;AAGT,QAAM,cAAmC,CAAC;AAE1C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAE7C,QAAA,IAAI,WAAW,GAAG,GAAG;AACvB,kBAAY,GAAG,IAAI;AACnB;AAAA,IAAA;AAIF,QAAI,eAAe,+CAAe,KAAK,CAAC,OAAO,GAAG,aAAa;AAI/D,QAAI,CAAC,gBAAgB,IAAI,WAAW,QAAQ,GAAG;AAC7C,qBAAe,+CAAe;AAAA,QAC5B,CAAC,OACC,GAAG,SAAS,iBAAiB,GAAG,KAAK,KAAK,WAAW,GAAG,KAAK,MAAM;AAAA;AAAA,IACvE;AAGE,QAAA,gBAAgB,aAAa,OAAO;AAGtC,YAAM,cAAc,aAAa;AAE7B,UAAA,MAAM,QAAQ,KAAK,GAAG;AACZ,oBAAA,WAAW,IAAI,MAAM;AAAA,UAAI,CAAC,iBACpC;AAAA,YACE;AAAA,YACA,aAAa;AAAA,YACb;AAAA;AAAA,UAAA;AAAA,QAEJ;AAAA,MACS,WAAA,SAAS,OAAO,UAAU,UAAU;AAC7C,oBAAY,WAAW,IAAI;AAAA,UACzB;AAAA,UACA,aAAa;AAAA,UACb;AAAA,QACF;AAAA,MAAA,OACK;AACL,oBAAY,WAAW,IAAI;AAAA,MAAA;AAE7B;AAAA,IAAA;AAII,UAAA,YAAY,aAAa,OAAO,GAAG;AACzC,gBAAY,SAAS,IAAI;AAAA,EAAA;AAGpB,SAAA;AACT;AAQgB,SAAA,yBACd,YACA,OACU;AACJ,QAAA,SAAS,mBAAmB,KAAK;AACnC,MAAA,CAAC,OAAO,QAAQ;AACX,WAAA;AAAA,EAAA;AAGT,SAAO,WAAW,IAAI,CAAC,cAAc,WAAW,OAAO,SAAS,CAAC;AACnE;AAQgB,SAAA,sBACd,eACA,OACQ;AACR,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EAAA;AAEH,QAAA,SAAS,mBAAmB,KAAK;AACvC,MAAI,CAAC,UAAU,CAAC,OAAO,QAAQ;AACtB,WAAA;AAAA,EAAA;AAIT,QAAM,QAAQ,cAAc,KAAK,EAAE,MAAM,KAAK;AACxC,QAAA,YAAY,MAAM,CAAC;AACzB,MAAI,CAAC,WAAW;AACP,WAAA;AAAA,EAAA;AAEH,QAAA,YAAY,MAAM,CAAC;AAEnB,QAAA,UAAU,WAAW,OAAO,SAAS;AAC3C,SAAO,YAAY,GAAG,OAAO,IAAI,SAAS,KAAK;AACjD;"}
@@ -87,8 +87,6 @@ export type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> =
87
87
  [K in keyof Schema]: Extract<StandardSchemaV1.InferOutput<Schema[K]>, null | undefined> extends never ? K : never;
88
88
  }[keyof Schema];
89
89
  export type ExcludedFields<IdField extends keyof any | undefined, ReadOnly extends readonly any[]> = IdField extends keyof any ? IdField | ReadOnly[number] : ReadOnly[number];
90
- export type InsertData<BT> = Partial<Record<string, any>>;
91
- export type UpdateData<BT> = Partial<Record<string, any>>;
92
90
  export type ExecuteOptions = {
93
91
  includeODataAnnotations?: boolean;
94
92
  skipValidation?: boolean;
@@ -99,61 +97,30 @@ export type ExecuteOptions = {
99
97
  };
100
98
  /**
101
99
  * Type for the fetchHandler callback function.
102
- * This type is re-exported to ensure proper type preservation in generated .d.ts files.
103
- *
104
- * **Usage Note**: Due to TypeScript limitations with optional properties in intersection types,
105
- * you may need to explicitly type fetchHandler parameters when using the built package:
100
+ * This is a convenience type export that matches the fetchHandler signature in FFetchOptions.
106
101
  *
107
102
  * @example
108
103
  * ```typescript
109
104
  * import type { FetchHandler } from '@proofkit/fmodata';
110
105
  *
111
- * await query.execute({
112
- * fetchHandler: ((input, init) => {
113
- * // TypeScript will infer types here
114
- * return fetch(input, init);
115
- * }) as FetchHandler
116
- * });
106
+ * const myFetchHandler: FetchHandler = (input, init) => {
107
+ * console.log('Custom fetch:', input);
108
+ * return fetch(input, init);
109
+ * };
117
110
  *
118
- * // Or with explicit parameters:
119
111
  * await query.execute({
120
- * fetchHandler: (input: RequestInfo | URL, init?: RequestInit) => {
121
- * return fetch(input, init);
122
- * }
112
+ * fetchHandler: myFetchHandler
123
113
  * });
124
114
  * ```
125
115
  */
126
116
  export type FetchHandler = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
127
- /**
128
- * Extended execute options that includes fetchHandler with proper typing.
129
- * This ensures the fetchHandler parameter types are preserved in generated declaration files.
130
- */
131
- export type ExecuteOptionsWithFetch = ExecuteOptions & {
132
- fetchHandler?: FetchHandler;
133
- };
134
- /**
135
- * Local re-export of key FFetchOptions properties to ensure type preservation.
136
- * This avoids relying on external package type resolution in the built package.
137
- */
138
- type LocalFFetchOptions = {
139
- timeout?: number;
140
- retries?: number;
141
- throwOnHttpError?: boolean;
142
- hooks?: any;
143
- /**
144
- * Custom fetch handler function.
145
- * The parameter types are explicitly defined inline to ensure proper type inference.
146
- */
147
- fetchHandler?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
148
- dedupe?: boolean;
149
- };
150
117
  /**
151
118
  * Combined type for execute() method options.
152
119
  *
153
- * Uses LocalFFetchOptions instead of importing from @fetchkit/ffetch to ensure
154
- * proper type inference in generated declaration files.
120
+ * Uses FFetchOptions from @fetchkit/ffetch to ensure proper type inference.
121
+ * FFetchOptions is re-exported in the package to ensure type availability in consuming packages.
155
122
  */
156
- export type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> = RequestInit & LocalFFetchOptions & ExecuteOptions & EO;
123
+ export type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> = RequestInit & FFetchOptions & ExecuteOptions & EO;
157
124
  /**
158
125
  * Get the Accept header value based on includeODataAnnotations option
159
126
  * @param includeODataAnnotations - Whether to include OData annotations
@@ -216,4 +183,3 @@ export type EntitySet = {
216
183
  $Type: string;
217
184
  };
218
185
  export type Metadata = Record<string, EntityType | EntitySet>;
219
- export {};
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nexport type Auth = { username: string; password: string } | { apiKey: string };\n\nexport interface ExecutableBuilder<T> {\n execute(): Promise<Result<T>>;\n getRequestConfig(): { method: string; url: string; body?: any };\n\n /**\n * Convert this builder to a native Request object for batch processing.\n * @param baseUrl - The base URL for the OData service\n * @param options - Optional execution options (e.g., includeODataAnnotations)\n * @returns A native Request object\n */\n toRequest(baseUrl: string, options?: ExecuteOptions): Request;\n\n /**\n * Process a raw Response object into a typed Result.\n * This allows builders to apply their own validation and transformation logic.\n * @param response - The native Response object from the batch operation\n * @param options - Optional execution options (e.g., skipValidation, includeODataAnnotations)\n * @returns A typed Result with the builder's expected return type\n */\n processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<Result<T>>;\n}\n\nexport interface ExecutionContext {\n _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<Result<T>>;\n _setUseEntityIds?(useEntityIds: boolean): void;\n _getUseEntityIds?(): boolean;\n _getBaseUrl?(): string;\n}\n\nexport type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = {\n [K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output>\n ? Output\n : never;\n};\n\nexport type WithSystemFields<T> =\n T extends Record<string, any>\n ? T & {\n ROWID: number;\n ROWMODID: number;\n }\n : never;\n\n// Helper type to exclude system fields from a union of keys\nexport type ExcludeSystemFields<T extends keyof any> = Exclude<\n T,\n \"ROWID\" | \"ROWMODID\"\n>;\n\n// Helper type to omit system fields from an object type\nexport type OmitSystemFields<T> = Omit<T, \"ROWID\" | \"ROWMODID\">;\n\n// OData record metadata fields (present on each record)\nexport type ODataRecordMetadata = {\n \"@id\": string;\n \"@editLink\": string;\n};\n\n// OData response wrapper (top-level, internal use only)\nexport type ODataListResponse<T> = {\n \"@context\": string;\n value: (T & ODataRecordMetadata)[];\n};\n\nexport type ODataSingleResponse<T> = T &\n ODataRecordMetadata & {\n \"@context\": string;\n };\n\n// OData response for single field values\nexport type ODataFieldResponse<T> = {\n \"@context\": string;\n value: T;\n};\n\n// Result pattern for execute responses\nexport type Result<T, E = import(\"./errors\").FMODataErrorType> =\n | { data: T; error: undefined }\n | { data: undefined; error: E };\n\n// Batch operation result types\nexport type BatchItemResult<T> = {\n data: T | undefined;\n error: import(\"./errors\").FMODataErrorType | undefined;\n status: number; // HTTP status code (0 for truncated)\n};\n\nexport type BatchResult<T extends readonly any[]> = {\n results: { [K in keyof T]: BatchItemResult<T[K]> };\n successCount: number;\n errorCount: number;\n truncated: boolean;\n firstErrorIndex: number | null;\n};\n\n// Make specific keys required, rest optional\nexport type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> &\n Required<Pick<T, Keys>>;\n\n// Extract keys from schema where validator doesn't allow null/undefined (auto-required fields)\nexport type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> =\n {\n [K in keyof Schema]: Extract<\n StandardSchemaV1.InferOutput<Schema[K]>,\n null | undefined\n > extends never\n ? K\n : never;\n }[keyof Schema];\n\n// Helper type to compute excluded fields (readOnly fields + idField)\nexport type ExcludedFields<\n IdField extends keyof any | undefined,\n ReadOnly extends readonly any[],\n> = IdField extends keyof any ? IdField | ReadOnly[number] : ReadOnly[number];\n\n// Helper type for InsertData computation\ntype ComputeInsertData<\n Schema extends Record<string, StandardSchemaV1>,\n IdField extends keyof Schema | undefined,\n Required extends readonly any[],\n ReadOnly extends readonly any[],\n> = [Required[number]] extends [keyof InferSchemaType<Schema>]\n ? Required extends readonly (keyof InferSchemaType<Schema>)[]\n ? MakeFieldsRequired<\n Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,\n Exclude<\n AutoRequiredKeys<Schema> | Required[number],\n ExcludedFields<IdField, ReadOnly>\n >\n >\n : MakeFieldsRequired<\n Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,\n Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>\n >\n : MakeFieldsRequired<\n Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,\n Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>\n >;\n\n// Legacy types for backward compatibility\n// Note: These types are deprecated. Use InsertDataFromFMTable and UpdateDataFromFMTable from the ORM API instead.\nexport type InsertData<BT> = Partial<Record<string, any>>;\nexport type UpdateData<BT> = Partial<Record<string, any>>;\n\nexport type ExecuteOptions = {\n includeODataAnnotations?: boolean;\n skipValidation?: boolean;\n /**\n * Overrides the default behavior of the database to use entity IDs (rather than field names) in THIS REQUEST ONLY\n */\n useEntityIds?: boolean;\n};\n\n/**\n * Type for the fetchHandler callback function.\n * This type is re-exported to ensure proper type preservation in generated .d.ts files.\n *\n * **Usage Note**: Due to TypeScript limitations with optional properties in intersection types,\n * you may need to explicitly type fetchHandler parameters when using the built package:\n *\n * @example\n * ```typescript\n * import type { FetchHandler } from '@proofkit/fmodata';\n *\n * await query.execute({\n * fetchHandler: ((input, init) => {\n * // TypeScript will infer types here\n * return fetch(input, init);\n * }) as FetchHandler\n * });\n *\n * // Or with explicit parameters:\n * await query.execute({\n * fetchHandler: (input: RequestInfo | URL, init?: RequestInit) => {\n * return fetch(input, init);\n * }\n * });\n * ```\n */\nexport type FetchHandler = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Extended execute options that includes fetchHandler with proper typing.\n * This ensures the fetchHandler parameter types are preserved in generated declaration files.\n */\nexport type ExecuteOptionsWithFetch = ExecuteOptions & {\n fetchHandler?: FetchHandler;\n};\n\n/**\n * Local re-export of key FFetchOptions properties to ensure type preservation.\n * This avoids relying on external package type resolution in the built package.\n */\ntype LocalFFetchOptions = {\n timeout?: number;\n retries?: number;\n throwOnHttpError?: boolean;\n hooks?: any; // Simplified for now\n /**\n * Custom fetch handler function.\n * The parameter types are explicitly defined inline to ensure proper type inference.\n */\n fetchHandler?: (\n input: RequestInfo | URL,\n init?: RequestInit,\n ) => Promise<Response>;\n dedupe?: boolean;\n};\n\n/**\n * Combined type for execute() method options.\n *\n * Uses LocalFFetchOptions instead of importing from @fetchkit/ffetch to ensure\n * proper type inference in generated declaration files.\n */\nexport type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> =\n RequestInit & LocalFFetchOptions & ExecuteOptions & EO;\n\n/**\n * Get the Accept header value based on includeODataAnnotations option\n * @param includeODataAnnotations - Whether to include OData annotations\n * @returns Accept header value\n */\nexport function getAcceptHeader(includeODataAnnotations?: boolean): string {\n return includeODataAnnotations === true\n ? \"application/json\"\n : \"application/json;odata.metadata=none\";\n}\n\nexport type ConditionallyWithODataAnnotations<\n T,\n IncludeODataAnnotations extends boolean,\n> = IncludeODataAnnotations extends true\n ? T & {\n \"@id\": string;\n \"@editLink\": string;\n }\n : T;\n\n// Helper type to extract schema from a TableOccurrence (legacy) or FMTable\nexport type ExtractSchemaFromOccurrence<Occ> = Occ extends {\n baseTable: { schema: infer S };\n}\n ? S extends Record<string, StandardSchemaV1>\n ? S\n : Record<string, StandardSchemaV1>\n : Record<string, StandardSchemaV1>;\n\nexport type GenericFieldMetadata = {\n $Nullable?: boolean;\n \"@Index\"?: boolean;\n \"@Calculation\"?: boolean;\n \"@Summary\"?: boolean;\n \"@Global\"?: boolean;\n \"@Org.OData.Core.V1.Permissions\"?: \"Org.OData.Core.V1.Permission@Read\";\n};\n\nexport type StringFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.String\";\n $DefaultValue?: \"USER\" | \"USERNAME\" | \"CURRENT_USER\";\n $MaxLength?: number;\n};\n\nexport type DecimalFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.Decimal\";\n \"@AutoGenerated\"?: boolean;\n};\n\nexport type DateFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.Date\";\n $DefaultValue?: \"CURDATE\" | \"CURRENT_DATE\";\n};\n\nexport type TimeOfDayFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.TimeOfDay\";\n $DefaultValue?: \"CURTIME\" | \"CURRENT_TIME\";\n};\n\nexport type DateTimeOffsetFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.Date\";\n $DefaultValue?: \"CURTIMESTAMP\" | \"CURRENT_TIMESTAMP\";\n \"@VersionId\"?: boolean;\n};\n\nexport type StreamFieldMetadata = {\n $Type: \"Edm.Stream\";\n $Nullable?: boolean;\n \"@EnclosedPath\": string;\n \"@ExternalOpenPath\": string;\n \"@ExternalSecurePath\"?: string;\n};\n\nexport type FieldMetadata =\n | StringFieldMetadata\n | DecimalFieldMetadata\n | DateFieldMetadata\n | TimeOfDayFieldMetadata\n | DateTimeOffsetFieldMetadata\n | StreamFieldMetadata;\n\nexport type EntityType = {\n $Kind: \"EntityType\";\n $Key: string[];\n} & Record<string, FieldMetadata>;\n\nexport type EntitySet = {\n $Kind: \"EntitySet\";\n $Type: string;\n};\n\nexport type Metadata = Record<string, EntityType | EntitySet>;\n"],"names":[],"mappings":"AA8OO,SAAS,gBAAgB,yBAA2C;AAClE,SAAA,4BAA4B,OAC/B,qBACA;AACN;"}
1
+ {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nexport type Auth = { username: string; password: string } | { apiKey: string };\n\nexport interface ExecutableBuilder<T> {\n execute(): Promise<Result<T>>;\n getRequestConfig(): { method: string; url: string; body?: any };\n\n /**\n * Convert this builder to a native Request object for batch processing.\n * @param baseUrl - The base URL for the OData service\n * @param options - Optional execution options (e.g., includeODataAnnotations)\n * @returns A native Request object\n */\n toRequest(baseUrl: string, options?: ExecuteOptions): Request;\n\n /**\n * Process a raw Response object into a typed Result.\n * This allows builders to apply their own validation and transformation logic.\n * @param response - The native Response object from the batch operation\n * @param options - Optional execution options (e.g., skipValidation, includeODataAnnotations)\n * @returns A typed Result with the builder's expected return type\n */\n processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<Result<T>>;\n}\n\nexport interface ExecutionContext {\n _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<Result<T>>;\n _setUseEntityIds?(useEntityIds: boolean): void;\n _getUseEntityIds?(): boolean;\n _getBaseUrl?(): string;\n}\n\nexport type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = {\n [K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output>\n ? Output\n : never;\n};\n\nexport type WithSystemFields<T> =\n T extends Record<string, any>\n ? T & {\n ROWID: number;\n ROWMODID: number;\n }\n : never;\n\n// Helper type to exclude system fields from a union of keys\nexport type ExcludeSystemFields<T extends keyof any> = Exclude<\n T,\n \"ROWID\" | \"ROWMODID\"\n>;\n\n// Helper type to omit system fields from an object type\nexport type OmitSystemFields<T> = Omit<T, \"ROWID\" | \"ROWMODID\">;\n\n// OData record metadata fields (present on each record)\nexport type ODataRecordMetadata = {\n \"@id\": string;\n \"@editLink\": string;\n};\n\n// OData response wrapper (top-level, internal use only)\nexport type ODataListResponse<T> = {\n \"@context\": string;\n value: (T & ODataRecordMetadata)[];\n};\n\nexport type ODataSingleResponse<T> = T &\n ODataRecordMetadata & {\n \"@context\": string;\n };\n\n// OData response for single field values\nexport type ODataFieldResponse<T> = {\n \"@context\": string;\n value: T;\n};\n\n// Result pattern for execute responses\nexport type Result<T, E = import(\"./errors\").FMODataErrorType> =\n | { data: T; error: undefined }\n | { data: undefined; error: E };\n\n// Batch operation result types\nexport type BatchItemResult<T> = {\n data: T | undefined;\n error: import(\"./errors\").FMODataErrorType | undefined;\n status: number; // HTTP status code (0 for truncated)\n};\n\nexport type BatchResult<T extends readonly any[]> = {\n results: { [K in keyof T]: BatchItemResult<T[K]> };\n successCount: number;\n errorCount: number;\n truncated: boolean;\n firstErrorIndex: number | null;\n};\n\n// Make specific keys required, rest optional\nexport type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> &\n Required<Pick<T, Keys>>;\n\n// Extract keys from schema where validator doesn't allow null/undefined (auto-required fields)\nexport type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> =\n {\n [K in keyof Schema]: Extract<\n StandardSchemaV1.InferOutput<Schema[K]>,\n null | undefined\n > extends never\n ? K\n : never;\n }[keyof Schema];\n\n// Helper type to compute excluded fields (readOnly fields + idField)\nexport type ExcludedFields<\n IdField extends keyof any | undefined,\n ReadOnly extends readonly any[],\n> = IdField extends keyof any ? IdField | ReadOnly[number] : ReadOnly[number];\n\n// Helper type for InsertData computation\ntype ComputeInsertData<\n Schema extends Record<string, StandardSchemaV1>,\n IdField extends keyof Schema | undefined,\n Required extends readonly any[],\n ReadOnly extends readonly any[],\n> = [Required[number]] extends [keyof InferSchemaType<Schema>]\n ? Required extends readonly (keyof InferSchemaType<Schema>)[]\n ? MakeFieldsRequired<\n Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,\n Exclude<\n AutoRequiredKeys<Schema> | Required[number],\n ExcludedFields<IdField, ReadOnly>\n >\n >\n : MakeFieldsRequired<\n Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,\n Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>\n >\n : MakeFieldsRequired<\n Omit<InferSchemaType<Schema>, ExcludedFields<IdField, ReadOnly>>,\n Exclude<AutoRequiredKeys<Schema>, ExcludedFields<IdField, ReadOnly>>\n >;\n\nexport type ExecuteOptions = {\n includeODataAnnotations?: boolean;\n skipValidation?: boolean;\n /**\n * Overrides the default behavior of the database to use entity IDs (rather than field names) in THIS REQUEST ONLY\n */\n useEntityIds?: boolean;\n};\n\n/**\n * Type for the fetchHandler callback function.\n * This is a convenience type export that matches the fetchHandler signature in FFetchOptions.\n *\n * @example\n * ```typescript\n * import type { FetchHandler } from '@proofkit/fmodata';\n *\n * const myFetchHandler: FetchHandler = (input, init) => {\n * console.log('Custom fetch:', input);\n * return fetch(input, init);\n * };\n *\n * await query.execute({\n * fetchHandler: myFetchHandler\n * });\n * ```\n */\nexport type FetchHandler = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Combined type for execute() method options.\n *\n * Uses FFetchOptions from @fetchkit/ffetch to ensure proper type inference.\n * FFetchOptions is re-exported in the package to ensure type availability in consuming packages.\n */\nexport type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> =\n RequestInit & FFetchOptions & ExecuteOptions & EO;\n\n/**\n * Get the Accept header value based on includeODataAnnotations option\n * @param includeODataAnnotations - Whether to include OData annotations\n * @returns Accept header value\n */\nexport function getAcceptHeader(includeODataAnnotations?: boolean): string {\n return includeODataAnnotations === true\n ? \"application/json\"\n : \"application/json;odata.metadata=none\";\n}\n\nexport type ConditionallyWithODataAnnotations<\n T,\n IncludeODataAnnotations extends boolean,\n> = IncludeODataAnnotations extends true\n ? T & {\n \"@id\": string;\n \"@editLink\": string;\n }\n : T;\n\n// Helper type to extract schema from a FMTable\nexport type ExtractSchemaFromOccurrence<Occ> = Occ extends {\n baseTable: { schema: infer S };\n}\n ? S extends Record<string, StandardSchemaV1>\n ? S\n : Record<string, StandardSchemaV1>\n : Record<string, StandardSchemaV1>;\n\nexport type GenericFieldMetadata = {\n $Nullable?: boolean;\n \"@Index\"?: boolean;\n \"@Calculation\"?: boolean;\n \"@Summary\"?: boolean;\n \"@Global\"?: boolean;\n \"@Org.OData.Core.V1.Permissions\"?: \"Org.OData.Core.V1.Permission@Read\";\n};\n\nexport type StringFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.String\";\n $DefaultValue?: \"USER\" | \"USERNAME\" | \"CURRENT_USER\";\n $MaxLength?: number;\n};\n\nexport type DecimalFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.Decimal\";\n \"@AutoGenerated\"?: boolean;\n};\n\nexport type DateFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.Date\";\n $DefaultValue?: \"CURDATE\" | \"CURRENT_DATE\";\n};\n\nexport type TimeOfDayFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.TimeOfDay\";\n $DefaultValue?: \"CURTIME\" | \"CURRENT_TIME\";\n};\n\nexport type DateTimeOffsetFieldMetadata = GenericFieldMetadata & {\n $Type: \"Edm.Date\";\n $DefaultValue?: \"CURTIMESTAMP\" | \"CURRENT_TIMESTAMP\";\n \"@VersionId\"?: boolean;\n};\n\nexport type StreamFieldMetadata = {\n $Type: \"Edm.Stream\";\n $Nullable?: boolean;\n \"@EnclosedPath\": string;\n \"@ExternalOpenPath\": string;\n \"@ExternalSecurePath\"?: string;\n};\n\nexport type FieldMetadata =\n | StringFieldMetadata\n | DecimalFieldMetadata\n | DateFieldMetadata\n | TimeOfDayFieldMetadata\n | DateTimeOffsetFieldMetadata\n | StreamFieldMetadata;\n\nexport type EntityType = {\n $Kind: \"EntityType\";\n $Key: string[];\n} & Record<string, FieldMetadata>;\n\nexport type EntitySet = {\n $Kind: \"EntitySet\";\n $Type: string;\n};\n\nexport type Metadata = Record<string, EntityType | EntitySet>;\n"],"names":[],"mappings":"AAqMO,SAAS,gBAAgB,yBAA2C;AAClE,SAAA,4BAA4B,OAC/B,qBACA;AACN;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofkit/fmodata",
3
- "version": "0.1.0-alpha.15",
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>",
@@ -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
-
@@ -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
+
@@ -9,11 +9,9 @@ import type {
9
9
  ExtractSchemaFromOccurrence,
10
10
  ExecuteMethodOptions,
11
11
  } from "../../types";
12
- import type { Filter } from "../../filter-types";
13
12
  import { RecordCountMismatchError } from "../../errors";
14
13
  import { type FFetchOptions } from "@fetchkit/ffetch";
15
14
  import {
16
- transformFieldName,
17
15
  transformFieldNamesArray,
18
16
  transformOrderByField,
19
17
  } from "../../transform";
@@ -66,7 +64,7 @@ export class QueryBuilder<
66
64
  | keyof InferSchemaOutputFromFMTable<Occ>
67
65
  | Record<
68
66
  string,
69
- Column<any, ExtractTableName<Occ>>
67
+ Column<any, any, ExtractTableName<Occ>>
70
68
  > = keyof InferSchemaOutputFromFMTable<Occ>,
71
69
  SingleMode extends "exact" | "maybe" | false = false,
72
70
  IsCount extends boolean = false,
@@ -152,7 +150,7 @@ export class QueryBuilder<
152
150
  private cloneWithChanges<
153
151
  NewSelected extends
154
152
  | keyof InferSchemaOutputFromFMTable<Occ>
155
- | Record<string, Column<any, ExtractTableName<Occ>>> = Selected,
153
+ | Record<string, Column<any, any, ExtractTableName<Occ>>> = Selected,
156
154
  NewSingle extends "exact" | "maybe" | false = SingleMode,
157
155
  NewCount extends boolean = IsCount,
158
156
  >(changes: {
@@ -207,7 +205,7 @@ export class QueryBuilder<
207
205
  * @returns QueryBuilder with updated selected fields
208
206
  */
209
207
  select<
210
- TSelect extends Record<string, Column<any, ExtractTableName<Occ>, false>>,
208
+ TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false>>,
211
209
  >(fields: TSelect): QueryBuilder<Occ, TSelect, SingleMode, IsCount, Expands> {
212
210
  const tableName = getTableName(this.occurrence);
213
211
  const { selectedFields, fieldMapping } = processSelectWithRenames(
@@ -225,131 +223,24 @@ export class QueryBuilder<
225
223
  });
226
224
  }
227
225
 
228
- /**
229
- * Transforms our filter format to odata-query's expected format
230
- * - Arrays of operators are converted to AND conditions
231
- * - Single operator objects pass through as-is
232
- * - Shorthand values are handled by odata-query
233
- */
234
- private transformFilter(
235
- filter: Filter<ExtractSchemaFromOccurrence<Occ>>,
236
- ): QueryOptions<InferSchemaOutputFromFMTable<Occ>>["filter"] {
237
- if (typeof filter === "string") {
238
- // Raw string filters pass through
239
- return filter;
240
- }
241
-
242
- if (Array.isArray(filter)) {
243
- // Array of filters - odata-query handles this as implicit AND
244
- return filter.map((f) => this.transformFilter(f as any)) as any;
245
- }
246
-
247
- // Check if it's a logical filter (and/or/not)
248
- if ("and" in filter || "or" in filter || "not" in filter) {
249
- const result: any = {};
250
- if ("and" in filter && Array.isArray(filter.and)) {
251
- result.and = filter.and.map((f: any) => this.transformFilter(f));
252
- }
253
- if ("or" in filter && Array.isArray(filter.or)) {
254
- result.or = filter.or.map((f: any) => this.transformFilter(f));
255
- }
256
- if ("not" in filter && filter.not) {
257
- result.not = this.transformFilter(filter.not as any);
258
- }
259
- return result;
260
- }
261
-
262
- // Transform field filters
263
- const result: any = {};
264
- const andConditions: any[] = [];
265
-
266
- for (const [field, value] of Object.entries(filter)) {
267
- // Transform field name to FMFID if using entity IDs AND the feature is enabled
268
- const shouldTransform = this.occurrence && this.databaseUseEntityIds;
269
- const fieldId = shouldTransform
270
- ? transformFieldName(field, this.occurrence!)
271
- : field;
272
-
273
- if (Array.isArray(value)) {
274
- // Array of operators - convert to AND conditions
275
- if (value.length === 1) {
276
- // Single operator in array - unwrap it
277
- result[fieldId] = value[0];
278
- } else {
279
- // Multiple operators - combine with AND
280
- // Create separate conditions for each operator
281
- for (const op of value) {
282
- andConditions.push({ [fieldId]: op });
283
- }
284
- }
285
- } else if (
286
- value &&
287
- typeof value === "object" &&
288
- !(value instanceof Date) &&
289
- !Array.isArray(value)
290
- ) {
291
- // Check if it's an operator object (has operator keys like eq, gt, etc.)
292
- const operatorKeys = [
293
- "eq",
294
- "ne",
295
- "gt",
296
- "ge",
297
- "lt",
298
- "le",
299
- "contains",
300
- "startswith",
301
- "endswith",
302
- "in",
303
- ];
304
- const isOperatorObject = operatorKeys.some((key) => key in value);
305
-
306
- if (isOperatorObject) {
307
- // Single operator object - pass through
308
- result[fieldId] = value;
309
- } else {
310
- // Regular object - might be nested filter, pass through
311
- result[fieldId] = value;
312
- }
313
- } else {
314
- // Primitive value (shorthand) - pass through
315
- result[fieldId] = value;
316
- }
317
- }
318
-
319
- // If we have AND conditions from arrays, combine them
320
- if (andConditions.length > 0) {
321
- if (Object.keys(result).length > 0) {
322
- // We have both regular fields and array-derived AND conditions
323
- // Combine everything with AND
324
- return { and: [...andConditions, result] };
325
- } else {
326
- // Only array-derived AND conditions
327
- return { and: andConditions };
328
- }
329
- }
330
-
331
- return result;
332
- }
333
-
334
- filter(
335
- filter: Filter<ExtractSchemaFromOccurrence<Occ>>,
336
- ): QueryBuilder<Occ, Selected, SingleMode, IsCount, Expands> {
337
- // Transform our filter format to odata-query's expected format
338
- this.queryOptions.filter = this.transformFilter(filter) as any;
339
- return this;
340
- }
341
-
342
226
  /**
343
227
  * Filter results using operator expressions (new ORM-style API).
344
228
  * Supports eq, gt, lt, and, or, etc. operators with Column references.
229
+ * Also supports raw OData filter strings as an escape hatch.
345
230
  *
346
231
  * @example
347
232
  * .where(eq(users.hobby, "reading"))
348
233
  * .where(and(eq(users.active, true), gt(users.age, 18)))
234
+ * .where("status eq 'active'") // Raw OData string escape hatch
349
235
  */
350
236
  where(
351
- expression: FilterExpression,
237
+ expression: FilterExpression | string,
352
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
+ }
353
244
  // Convert FilterExpression to OData filter string
354
245
  const filterString = expression.toODataFilter(this.databaseUseEntityIds);
355
246
  this.queryOptions.filter = filterString;
@@ -384,13 +275,13 @@ export class QueryBuilder<
384
275
  ...orderByArgs:
385
276
  | [
386
277
  | TypeSafeOrderBy<InferSchemaOutputFromFMTable<Occ>>
387
- | Column<any, ExtractTableName<Occ>>
278
+ | Column<any, any, ExtractTableName<Occ>>
388
279
  | OrderByExpression<ExtractTableName<Occ>>,
389
280
  ]
390
281
  | [
391
- Column<any, ExtractTableName<Occ>>,
282
+ Column<any, any, ExtractTableName<Occ>>,
392
283
  ...Array<
393
- | Column<any, ExtractTableName<Occ>>
284
+ | Column<any, any, ExtractTableName<Occ>>
394
285
  | OrderByExpression<ExtractTableName<Occ>>
395
286
  >,
396
287
  ]
@@ -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<
@@ -44,7 +44,7 @@ type ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;
44
44
  * For each key in the select object, extract the type from the corresponding Column.
45
45
  */
46
46
  type MapSelectToReturnType<
47
- TSelect extends Record<string, Column<any, any>>,
47
+ TSelect extends Record<string, Column<any, any, any, any>>,
48
48
  TSchema extends Record<string, any>,
49
49
  > = {
50
50
  [K in keyof TSelect]: ExtractColumnType<TSelect[K]>;
@@ -57,12 +57,12 @@ export type RecordReturnType<
57
57
  FieldKey extends keyof Schema,
58
58
  Selected extends
59
59
  | keyof Schema
60
- | Record<string, Column<any, ExtractTableName<FMTable<any, any>>>>,
60
+ | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,
61
61
  Expands extends ExpandedRelations,
62
62
  > = IsSingleField extends true
63
63
  ? Schema[FieldKey]
64
64
  : // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions
65
- [Selected] extends [Record<string, Column<any, any>>]
65
+ [Selected] extends [Record<string, Column<any, any, any, any>>]
66
66
  ? MapSelectToReturnType<Selected, Schema> & {
67
67
  [K in keyof Expands]: Pick<
68
68
  Expands[K]["schema"],
@@ -89,7 +89,7 @@ export class RecordBuilder<
89
89
  | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>
90
90
  | Record<
91
91
  string,
92
- Column<any, ExtractTableName<NonNullable<Occ>>>
92
+ Column<any, any, ExtractTableName<NonNullable<Occ>>>
93
93
  > = keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,
94
94
  Expands extends ExpandedRelations = {},
95
95
  > implements
@@ -169,7 +169,7 @@ export class RecordBuilder<
169
169
  | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>
170
170
  | Record<
171
171
  string,
172
- Column<any, ExtractTableName<NonNullable<Occ>>>
172
+ Column<any, any, ExtractTableName<NonNullable<Occ>>>
173
173
  > = Selected,
174
174
  >(changes: {
175
175
  selectedFields?: string[];
@@ -246,7 +246,10 @@ export class RecordBuilder<
246
246
  * @returns RecordBuilder with updated selected fields
247
247
  */
248
248
  select<
249
- TSelect extends Record<string, Column<any, ExtractTableName<Occ>, false>>,
249
+ TSelect extends Record<
250
+ string,
251
+ Column<any, any, ExtractTableName<Occ>, false>
252
+ >,
250
253
  >(fields: TSelect): RecordBuilder<Occ, false, FieldKey, TSelect, Expands> {
251
254
  const tableName = getTableName(this.table);
252
255
  const { selectedFields, fieldMapping } = processSelectWithRenames(
package/src/index.ts CHANGED
@@ -74,8 +74,6 @@ export type {
74
74
  BatchResult,
75
75
  BatchItemResult,
76
76
  InferSchemaType,
77
- InsertData,
78
- UpdateData,
79
77
  ODataRecordMetadata,
80
78
  Metadata,
81
79
  FetchHandler,
@@ -83,19 +81,7 @@ export type {
83
81
  ExecuteOptions,
84
82
  } from "./types";
85
83
 
86
- // Filter types
87
- export type {
88
- Filter,
89
- TypedFilter,
90
- FieldFilter,
91
- StringOperators,
92
- NumberOperators,
93
- BooleanOperators,
94
- DateOperators,
95
- LogicalFilter,
96
- } from "./filter-types";
97
-
98
- // Re-export ffetch errors
84
+ // Re-export ffetch errors and types
99
85
  export {
100
86
  TimeoutError,
101
87
  AbortError,
@@ -104,6 +90,8 @@ export {
104
90
  CircuitOpenError,
105
91
  } from "@fetchkit/ffetch";
106
92
 
93
+ export type { FFetchOptions } from "@fetchkit/ffetch";
94
+
107
95
  // Export our errors
108
96
  export {
109
97
  FMODataError,