@proofkit/fmodata 0.1.0-beta.23 → 0.1.0-beta.24

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 (35) hide show
  1. package/README.md +4 -4
  2. package/dist/esm/client/batch-builder.js.map +1 -1
  3. package/dist/esm/client/batch-request.js.map +1 -1
  4. package/dist/esm/client/builders/default-select.js.map +1 -1
  5. package/dist/esm/client/builders/expand-builder.js.map +1 -1
  6. package/dist/esm/client/builders/query-string-builder.js.map +1 -1
  7. package/dist/esm/client/builders/response-processor.js.map +1 -1
  8. package/dist/esm/client/builders/select-mixin.js.map +1 -1
  9. package/dist/esm/client/builders/select-utils.js.map +1 -1
  10. package/dist/esm/client/builders/table-utils.js.map +1 -1
  11. package/dist/esm/client/database.js.map +1 -1
  12. package/dist/esm/client/delete-builder.js.map +1 -1
  13. package/dist/esm/client/entity-set.js +18 -2
  14. package/dist/esm/client/entity-set.js.map +1 -1
  15. package/dist/esm/client/error-parser.js.map +1 -1
  16. package/dist/esm/client/filemaker-odata.js.map +1 -1
  17. package/dist/esm/client/insert-builder.js.map +1 -1
  18. package/dist/esm/client/query/query-builder.js.map +1 -1
  19. package/dist/esm/client/query/url-builder.js.map +1 -1
  20. package/dist/esm/client/record-builder.js.map +1 -1
  21. package/dist/esm/client/sanitize-json.js.map +1 -1
  22. package/dist/esm/client/schema-manager.js.map +1 -1
  23. package/dist/esm/client/update-builder.js.map +1 -1
  24. package/dist/esm/client/webhook-builder.js.map +1 -1
  25. package/dist/esm/errors.js.map +1 -1
  26. package/dist/esm/logger.js.map +1 -1
  27. package/dist/esm/orm/column.js.map +1 -1
  28. package/dist/esm/orm/field-builders.js.map +1 -1
  29. package/dist/esm/orm/operators.js.map +1 -1
  30. package/dist/esm/orm/table.js.map +1 -1
  31. package/dist/esm/transform.js.map +1 -1
  32. package/dist/esm/types.js.map +1 -1
  33. package/dist/esm/validation.js.map +1 -1
  34. package/package.json +16 -19
  35. package/src/client/entity-set.ts +22 -2
@@ -1 +1 @@
1
- {"version":3,"file":"url-builder.js","sources":["../../../../src/client/query/url-builder.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { getTableName } from \"../../orm/table\";\nimport type { ExecutionContext } from \"../../types\";\nimport { resolveTableId } from \"../builders/table-utils\";\n\n/**\n * Configuration for navigation from RecordBuilder or EntitySet\n */\nexport interface NavigationConfig {\n recordId?: string | number;\n relation: string;\n sourceTableName: string;\n baseRelation?: string; // For chained navigations from navigated EntitySets\n basePath?: string; // Full base path for chained entity set navigations\n}\n\n/**\n * Builds OData query URLs for different navigation modes.\n * Handles:\n * - Record navigation: /database/sourceTable('recordId')/relation\n * - Entity set navigation: /database/sourceTable/relation\n * - Count endpoint: /database/tableId/$count\n * - Standard queries: /database/tableId\n */\nexport class QueryUrlBuilder {\n private readonly databaseName: string;\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n private readonly occurrence: FMTable<any, any>;\n private readonly context: ExecutionContext;\n\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n constructor(databaseName: string, occurrence: FMTable<any, any>, context: ExecutionContext) {\n this.databaseName = databaseName;\n this.occurrence = occurrence;\n this.context = context;\n }\n\n /**\n * Builds the full URL for a query request.\n *\n * @param queryString - The OData query string (e.g., \"?$filter=...&$select=...\")\n * @param options - Options including whether this is a count query, useEntityIds override, and navigation config\n */\n build(\n queryString: string,\n options: {\n isCount?: boolean;\n useEntityIds?: boolean;\n navigation?: NavigationConfig;\n },\n ): string {\n const tableId = resolveTableId(this.occurrence, getTableName(this.occurrence), this.context, options.useEntityIds);\n\n const navigation = options.navigation;\n if (navigation?.recordId && navigation?.relation) {\n return this.buildRecordNavigation(queryString, tableId, navigation);\n }\n if (navigation?.relation) {\n return this.buildEntitySetNavigation(queryString, tableId, navigation);\n }\n if (options.isCount) {\n return `/${this.databaseName}/${tableId}/$count${queryString}`;\n }\n return `/${this.databaseName}/${tableId}${queryString}`;\n }\n\n /**\n * Builds URL for record navigation: /database/sourceTable('recordId')/relation\n * or /database/sourceTable/baseRelation('recordId')/relation for chained navigations\n */\n private buildRecordNavigation(queryString: string, _tableId: string, navigation: NavigationConfig): string {\n const { sourceTableName, baseRelation, recordId, relation } = navigation;\n const base = baseRelation\n ? `${sourceTableName}/${baseRelation}('${recordId}')`\n : `${sourceTableName}('${recordId}')`;\n return `/${this.databaseName}/${base}/${relation}${queryString}`;\n }\n\n /**\n * Builds URL for entity set navigation: /database/sourceTable/relation\n * or /database/basePath/relation for chained navigations\n */\n private buildEntitySetNavigation(queryString: string, _tableId: string, navigation: NavigationConfig): string {\n const { sourceTableName, basePath, relation } = navigation;\n const base = basePath || sourceTableName;\n return `/${this.databaseName}/${base}/${relation}${queryString}`;\n }\n\n /**\n * Builds a query string path (without database prefix) for getQueryString().\n * Used when the full URL is not needed.\n */\n buildPath(queryString: string, options?: { useEntityIds?: boolean; navigation?: NavigationConfig }): string {\n const useEntityIds = options?.useEntityIds;\n const navigation = options?.navigation;\n const tableId = resolveTableId(this.occurrence, getTableName(this.occurrence), this.context, useEntityIds);\n\n if (navigation?.recordId && navigation?.relation) {\n const { sourceTableName, baseRelation, recordId, relation } = navigation;\n const base = baseRelation\n ? `${sourceTableName}/${baseRelation}('${recordId}')`\n : `${sourceTableName}('${recordId}')`;\n return queryString ? `/${base}/${relation}${queryString}` : `/${base}/${relation}`;\n }\n if (navigation?.relation) {\n const { sourceTableName, basePath, relation } = navigation;\n const base = basePath || sourceTableName;\n return queryString ? `/${base}/${relation}${queryString}` : `/${base}/${relation}`;\n }\n return queryString ? `/${tableId}${queryString}` : `/${tableId}`;\n }\n\n /**\n * Build URL for record operations (single record by ID).\n * Used by RecordBuilder to build URLs like /database/table('id').\n *\n * @param recordId - The record ID\n * @param queryString - The OData query string (e.g., \"?$select=...\")\n * @param options - Options including operation type and useEntityIds override\n */\n buildRecordUrl(\n recordId: string | number,\n queryString: string,\n options?: {\n operation?: \"getSingleField\";\n operationParam?: string;\n useEntityIds?: boolean;\n isNavigateFromEntitySet?: boolean;\n navigateSourceTableName?: string;\n navigateRelation?: string;\n },\n ): string {\n const tableId = resolveTableId(this.occurrence, getTableName(this.occurrence), this.context, options?.useEntityIds);\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n let url: string;\n if (options?.isNavigateFromEntitySet && options.navigateSourceTableName && options.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${options.navigateSourceTableName}/${options.navigateRelation}('${recordId}')`;\n } else {\n // Normal record: /tableName('recordId') - use FMTID if configured\n url = `/${this.databaseName}/${tableId}('${recordId}')`;\n }\n\n if (options?.operation === \"getSingleField\" && options.operationParam) {\n url += `/${options.operationParam}`;\n }\n\n return url + queryString;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"url-builder.js","sources":["../../../../src/client/query/url-builder.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { getTableName } from \"../../orm/table\";\nimport type { ExecutionContext } from \"../../types\";\nimport { resolveTableId } from \"../builders/table-utils\";\n\n/**\n * Configuration for navigation from RecordBuilder or EntitySet\n */\nexport interface NavigationConfig {\n recordId?: string | number;\n relation: string;\n sourceTableName: string;\n baseRelation?: string; // For chained navigations from navigated EntitySets\n basePath?: string; // Full base path for chained entity set navigations\n}\n\n/**\n * Builds OData query URLs for different navigation modes.\n * Handles:\n * - Record navigation: /database/sourceTable('recordId')/relation\n * - Entity set navigation: /database/sourceTable/relation\n * - Count endpoint: /database/tableId/$count\n * - Standard queries: /database/tableId\n */\nexport class QueryUrlBuilder {\n private readonly databaseName: string;\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n private readonly occurrence: FMTable<any, any>;\n private readonly context: ExecutionContext;\n\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n constructor(databaseName: string, occurrence: FMTable<any, any>, context: ExecutionContext) {\n this.databaseName = databaseName;\n this.occurrence = occurrence;\n this.context = context;\n }\n\n /**\n * Builds the full URL for a query request.\n *\n * @param queryString - The OData query string (e.g., \"?$filter=...&$select=...\")\n * @param options - Options including whether this is a count query, useEntityIds override, and navigation config\n */\n build(\n queryString: string,\n options: {\n isCount?: boolean;\n useEntityIds?: boolean;\n navigation?: NavigationConfig;\n },\n ): string {\n const tableId = resolveTableId(this.occurrence, getTableName(this.occurrence), this.context, options.useEntityIds);\n\n const navigation = options.navigation;\n if (navigation?.recordId && navigation?.relation) {\n return this.buildRecordNavigation(queryString, tableId, navigation);\n }\n if (navigation?.relation) {\n return this.buildEntitySetNavigation(queryString, tableId, navigation);\n }\n if (options.isCount) {\n return `/${this.databaseName}/${tableId}/$count${queryString}`;\n }\n return `/${this.databaseName}/${tableId}${queryString}`;\n }\n\n /**\n * Builds URL for record navigation: /database/sourceTable('recordId')/relation\n * or /database/sourceTable/baseRelation('recordId')/relation for chained navigations\n */\n private buildRecordNavigation(queryString: string, _tableId: string, navigation: NavigationConfig): string {\n const { sourceTableName, baseRelation, recordId, relation } = navigation;\n const base = baseRelation\n ? `${sourceTableName}/${baseRelation}('${recordId}')`\n : `${sourceTableName}('${recordId}')`;\n return `/${this.databaseName}/${base}/${relation}${queryString}`;\n }\n\n /**\n * Builds URL for entity set navigation: /database/sourceTable/relation\n * or /database/basePath/relation for chained navigations\n */\n private buildEntitySetNavigation(queryString: string, _tableId: string, navigation: NavigationConfig): string {\n const { sourceTableName, basePath, relation } = navigation;\n const base = basePath || sourceTableName;\n return `/${this.databaseName}/${base}/${relation}${queryString}`;\n }\n\n /**\n * Builds a query string path (without database prefix) for getQueryString().\n * Used when the full URL is not needed.\n */\n buildPath(queryString: string, options?: { useEntityIds?: boolean; navigation?: NavigationConfig }): string {\n const useEntityIds = options?.useEntityIds;\n const navigation = options?.navigation;\n const tableId = resolveTableId(this.occurrence, getTableName(this.occurrence), this.context, useEntityIds);\n\n if (navigation?.recordId && navigation?.relation) {\n const { sourceTableName, baseRelation, recordId, relation } = navigation;\n const base = baseRelation\n ? `${sourceTableName}/${baseRelation}('${recordId}')`\n : `${sourceTableName}('${recordId}')`;\n return queryString ? `/${base}/${relation}${queryString}` : `/${base}/${relation}`;\n }\n if (navigation?.relation) {\n const { sourceTableName, basePath, relation } = navigation;\n const base = basePath || sourceTableName;\n return queryString ? `/${base}/${relation}${queryString}` : `/${base}/${relation}`;\n }\n return queryString ? `/${tableId}${queryString}` : `/${tableId}`;\n }\n\n /**\n * Build URL for record operations (single record by ID).\n * Used by RecordBuilder to build URLs like /database/table('id').\n *\n * @param recordId - The record ID\n * @param queryString - The OData query string (e.g., \"?$select=...\")\n * @param options - Options including operation type and useEntityIds override\n */\n buildRecordUrl(\n recordId: string | number,\n queryString: string,\n options?: {\n operation?: \"getSingleField\";\n operationParam?: string;\n useEntityIds?: boolean;\n isNavigateFromEntitySet?: boolean;\n navigateSourceTableName?: string;\n navigateRelation?: string;\n },\n ): string {\n const tableId = resolveTableId(this.occurrence, getTableName(this.occurrence), this.context, options?.useEntityIds);\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n let url: string;\n if (options?.isNavigateFromEntitySet && options.navigateSourceTableName && options.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${options.navigateSourceTableName}/${options.navigateRelation}('${recordId}')`;\n } else {\n // Normal record: /tableName('recordId') - use FMTID if configured\n url = `/${this.databaseName}/${tableId}('${recordId}')`;\n }\n\n if (options?.operation === \"getSingleField\" && options.operationParam) {\n url += `/${options.operationParam}`;\n }\n\n return url + queryString;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"record-builder.js","sources":["../../../src/client/record-builder.ts"],"sourcesContent":["/** biome-ignore-all lint/complexity/noBannedTypes: Empty object type represents no expands by default */\nimport type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport { createLogger, type InternalLogger } from \"../logger\";\nimport type { Column } from \"../orm/column\";\nimport type { ExtractTableName, FMTable, InferSchemaOutputFromFMTable, ValidExpandTarget } from \"../orm/table\";\nimport { getNavigationPaths, getTableName } from \"../orm/table\";\nimport type {\n ConditionallyWithODataAnnotations,\n ConditionallyWithSpecialColumns,\n ExecutableBuilder,\n ExecuteMethodOptions,\n ExecuteOptions,\n ExecutionContext,\n NormalizeIncludeSpecialColumns,\n ODataFieldResponse,\n Result,\n} from \"../types\";\nimport {\n buildSelectExpandQueryString,\n createODataRequest,\n ExpandBuilder,\n type ExpandConfig,\n type ExpandedRelations,\n getSchemaFromTable,\n mergeExecuteOptions,\n processODataResponse,\n processSelectWithRenames,\n resolveTableId,\n} from \"./builders/index\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport type { ResolveExpandedRelations, SystemColumnsFromOption, SystemColumnsOption } from \"./query/types\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { safeJsonParse } from \"./sanitize-json\";\n\n/**\n * Extract the value type from a Column.\n * This uses the phantom type stored in Column to get the actual value type.\n */\n// biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer\ntype ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;\n\n/**\n * Map a select object to its return type.\n * For each key in the select object, extract the type from the corresponding Column.\n */\ntype MapSelectToReturnType<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n TSelect extends Record<string, Column<any, any, any, any>>,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape\n _TSchema extends Record<string, any>,\n> = {\n [K in keyof TSelect]: ExtractColumnType<TSelect[K]>;\n};\n\n// Return type for RecordBuilder execute\nexport type RecordReturnType<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape\n Schema extends Record<string, any>,\n IsSingleField extends boolean,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n FieldColumn extends Column<any, any, any, any> | undefined,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration, accepts any FMTable\n Selected extends keyof Schema | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,\n Expands extends ExpandedRelations,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> = IsSingleField extends true\n ? // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer\n FieldColumn extends Column<infer TOutput, any, any, any>\n ? TOutput\n : never\n : // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n [Selected] extends [Record<string, Column<any, any, any, any>>]\n ? MapSelectToReturnType<Selected, Schema> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>\n : // Use tuple wrapping to prevent distribution over union of keys\n [Selected] extends [keyof Schema]\n ? Pick<Schema, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>\n : never;\n\nexport class RecordBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration, default allows untyped tables\n Occ extends FMTable<any, any> = FMTable<any, any>,\n IsSingleField extends boolean = false,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n FieldColumn extends Column<any, any, any, any> | undefined = undefined,\n Selected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = keyof InferSchemaOutputFromFMTable<\n NonNullable<Occ>\n >,\n Expands extends ExpandedRelations = {},\n DatabaseIncludeSpecialColumns extends boolean = false,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> implements\n ExecutableBuilder<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >\n >\n{\n private readonly table: Occ;\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly recordId: string | number;\n private readonly operation?: \"getSingleField\" | \"navigate\";\n private readonly operationParam?: string;\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n private readonly operationColumn?: Column<any, any, any, any>;\n private readonly isNavigateFromEntitySet?: boolean;\n private readonly navigateRelation?: string;\n private readonly navigateSourceTableName?: string;\n\n private readonly databaseUseEntityIds: boolean;\n private readonly databaseIncludeSpecialColumns: boolean;\n\n // Properties for select/expand support\n private readonly selectedFields?: string[];\n private readonly expandConfigs: ExpandConfig[] = [];\n // Mapping from field names to output keys (for renamed fields in select)\n private readonly fieldMapping?: Record<string, string>;\n // System columns requested via select() second argument\n private readonly systemColumns?: SystemColumnsOption;\n\n private readonly logger: InternalLogger;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n recordId: string | number;\n databaseUseEntityIds?: boolean;\n databaseIncludeSpecialColumns?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.recordId = config.recordId;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;\n this.logger = config.context?._getLogger?.() ?? createLogger();\n }\n\n /**\n * Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options\n */\n private mergeExecuteOptions(options?: RequestInit & FFetchOptions & ExecuteOptions): RequestInit &\n FFetchOptions & {\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n } {\n const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);\n return {\n ...merged,\n includeSpecialColumns: options?.includeSpecialColumns ?? this.databaseIncludeSpecialColumns,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n if (!this.table) {\n throw new Error(\"Table occurrence is required\");\n }\n return resolveTableId(this.table, getTableName(this.table), this.context, useEntityIds);\n }\n\n /**\n * Creates a new RecordBuilder with modified configuration.\n * Used by select() to create new instances.\n */\n private cloneWithChanges<\n NewSelected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = Selected,\n NewSystemCols extends SystemColumnsOption | undefined = SystemCols,\n >(changes: {\n selectedFields?: string[];\n fieldMapping?: Record<string, string>;\n systemColumns?: NewSystemCols;\n }): RecordBuilder<Occ, false, FieldColumn, NewSelected, Expands, DatabaseIncludeSpecialColumns, NewSystemCols> {\n const newBuilder = new RecordBuilder<\n Occ,\n false,\n FieldColumn,\n NewSelected,\n Expands,\n DatabaseIncludeSpecialColumns,\n NewSystemCols\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n // Use type assertion to allow assignment to readonly properties on new instance\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n mutableBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;\n mutableBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;\n mutableBuilder.systemColumns = changes.systemColumns !== undefined ? changes.systemColumns : this.systemColumns;\n mutableBuilder.expandConfigs = [...this.expandConfigs];\n // Preserve navigation context\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n mutableBuilder.operationColumn = this.operationColumn;\n return newBuilder;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n getSingleField<TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>>(\n column: TColumn,\n ): RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {},\n DatabaseIncludeSpecialColumns\n > {\n // Runtime validation: ensure column is from the correct table\n const tableName = getTableName(this.table);\n if (!column.isFromTable(tableName)) {\n throw new Error(`Column ${column.toString()} is not from table ${tableName}`);\n }\n\n const newBuilder = new RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {},\n DatabaseIncludeSpecialColumns\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n // Use type assertion to allow assignment to readonly properties on new instance\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n mutableBuilder.operation = \"getSingleField\";\n mutableBuilder.operationColumn = column;\n mutableBuilder.operationParam = column.getFieldIdentifier(this.databaseUseEntityIds);\n // Preserve navigation context\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n return newBuilder;\n }\n\n /**\n * Select fields using column references.\n * Allows renaming fields by using different keys in the object.\n * Container fields cannot be selected and will cause a type error.\n *\n * @example\n * db.from(contacts).get(\"uuid\").select({\n * name: contacts.name,\n * userEmail: contacts.email // renamed!\n * })\n *\n * @example\n * // Include system columns (ROWID, ROWMODID) when using select()\n * db.from(contacts).get(\"uuid\").select(\n * { name: contacts.name },\n * { ROWID: true, ROWMODID: true }\n * )\n *\n * @param fields - Object mapping output keys to column references (container fields excluded)\n * @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)\n * @returns RecordBuilder with updated selected fields\n */\n select<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false>>,\n TSystemCols extends SystemColumnsOption = {},\n >(\n fields: TSelect,\n systemColumns?: TSystemCols,\n ): RecordBuilder<Occ, false, FieldColumn, TSelect, Expands, DatabaseIncludeSpecialColumns, TSystemCols> {\n const tableName = getTableName(this.table);\n const { selectedFields, fieldMapping } = processSelectWithRenames(fields, tableName, this.logger);\n\n // Add system columns to selectedFields if requested\n const finalSelectedFields = [...selectedFields];\n if (systemColumns?.ROWID) {\n finalSelectedFields.push(\"ROWID\");\n }\n if (systemColumns?.ROWMODID) {\n finalSelectedFields.push(\"ROWMODID\");\n }\n\n return this.cloneWithChanges({\n selectedFields: finalSelectedFields,\n fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : undefined,\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type parameter\n systemColumns: systemColumns as any,\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for complex generic return type\n }) as any;\n }\n\n /**\n * Expand a navigation property to include related records.\n * Supports nested select, filter, orderBy, and expand operations.\n *\n * @example\n * ```typescript\n * // Simple expand with FMTable object\n * const contact = await db.from(contacts).get(\"uuid\").expand(users).execute();\n *\n * // Expand with select\n * const contact = await db.from(contacts).get(\"uuid\")\n * .expand(users, b => b.select({ username: users.username, email: users.email }))\n * .execute();\n * ```\n */\n expand<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n TargetTable extends FMTable<any, any>,\n TSelected extends\n | keyof InferSchemaOutputFromFMTable<TargetTable>\n | Record<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n Column<any, any, ExtractTableName<TargetTable>>\n > = keyof InferSchemaOutputFromFMTable<TargetTable>,\n TNestedExpands extends ExpandedRelations = {},\n >(\n targetTable: ValidExpandTarget<Occ, TargetTable>,\n callback?: (\n builder: QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}>,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n ) => QueryBuilder<TargetTable, TSelected, any, any, TNestedExpands>,\n ): RecordBuilder<\n Occ,\n false,\n FieldColumn,\n Selected,\n Expands & {\n [K in ExtractTableName<TargetTable>]: {\n schema: InferSchemaOutputFromFMTable<TargetTable>;\n selected: TSelected;\n nested: TNestedExpands;\n };\n },\n DatabaseIncludeSpecialColumns,\n SystemCols\n > {\n // Create new builder with updated types\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any ExpandedRelations\n const newBuilder = new RecordBuilder<Occ, false, FieldColumn, Selected, any, DatabaseIncludeSpecialColumns>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n\n // Use type assertion to allow assignment to readonly properties on new instance\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n // Copy existing state\n mutableBuilder.selectedFields = this.selectedFields;\n mutableBuilder.fieldMapping = this.fieldMapping;\n mutableBuilder.systemColumns = this.systemColumns;\n mutableBuilder.expandConfigs = [...this.expandConfigs];\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n mutableBuilder.operationColumn = this.operationColumn;\n\n // Use ExpandBuilder.processExpand to handle the expand logic\n const expandBuilder = new ExpandBuilder(this.databaseUseEntityIds, this.logger);\n type TargetBuilder = QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}>;\n const expandConfig = expandBuilder.processExpand<TargetTable, TargetBuilder>(\n targetTable,\n this.table ?? undefined,\n callback as ((builder: TargetBuilder) => TargetBuilder) | undefined,\n () =>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n new QueryBuilder<TargetTable, any, any, any, any, DatabaseIncludeSpecialColumns, undefined>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n }),\n );\n\n mutableBuilder.expandConfigs.push(expandConfig);\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion needed as expand changes generic parameters in complex way that TypeScript cannot infer\n return newBuilder as any;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n navigate<TargetTable extends FMTable<any, any>>(\n targetTable: ValidExpandTarget<Occ, TargetTable>,\n ): QueryBuilder<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {},\n DatabaseIncludeSpecialColumns,\n undefined\n > {\n // Extract name and validate\n const relationName = getTableName(targetTable);\n\n // Runtime validation: Check if relation name is in navigationPaths\n if (this.table) {\n const navigationPaths = getNavigationPaths(this.table);\n if (navigationPaths && !navigationPaths.includes(relationName)) {\n this.logger.warn(\n `Cannot navigate to \"${relationName}\". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(\", \") : \"none\"}`,\n );\n }\n }\n\n // Create QueryBuilder with target table\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n const builder = new QueryBuilder<TargetTable, any, any, any, any, DatabaseIncludeSpecialColumns, undefined>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n\n // Store the navigation info - we'll use it in execute\n // Use relation name as-is (entity ID handling is done in QueryBuilder)\n const relationId = relationName;\n\n // If this RecordBuilder came from a navigated EntitySet, we need to preserve that base path\n let sourceTableName: string;\n let baseRelation: string | undefined;\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // Build the base path: /sourceTable/relation('recordId')/newRelation\n sourceTableName = this.navigateSourceTableName;\n baseRelation = this.navigateRelation;\n } else {\n // Normal record navigation: /tableName('recordId')/relation\n // Use table ID if available, otherwise table name\n if (!this.table) {\n throw new Error(\"Table occurrence is required for navigation\");\n }\n sourceTableName = resolveTableId(this.table, getTableName(this.table), this.context, this.databaseUseEntityIds);\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n (builder as any).navigation = {\n recordId: this.recordId,\n relation: relationId,\n sourceTableName,\n baseRelation,\n };\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n (builder as any).navigation = {\n recordId: this.recordId,\n relation: relationId,\n sourceTableName,\n baseRelation,\n };\n\n return builder;\n }\n\n /**\n * Builds the complete query string including $select and $expand parameters.\n */\n private buildQueryString(includeSpecialColumns?: boolean, useEntityIds?: boolean): string {\n // Use merged includeSpecialColumns if provided, otherwise use database-level default\n const finalIncludeSpecialColumns = includeSpecialColumns ?? this.databaseIncludeSpecialColumns;\n // Use merged useEntityIds if provided, otherwise use database-level default\n const finalUseEntityIds = useEntityIds ?? this.databaseUseEntityIds;\n\n return buildSelectExpandQueryString({\n selectedFields: this.selectedFields,\n expandConfigs: this.expandConfigs,\n table: this.table,\n useEntityIds: finalUseEntityIds,\n logger: this.logger,\n includeSpecialColumns: finalIncludeSpecialColumns,\n });\n }\n\n async execute<EO extends ExecuteOptions>(\n options?: ExecuteMethodOptions<EO>,\n ): Promise<\n Result<\n ConditionallyWithODataAnnotations<\n ConditionallyWithSpecialColumns<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >,\n // Use the merged value: if explicitly provided in options, use that; otherwise use database default\n NormalizeIncludeSpecialColumns<EO[\"includeSpecialColumns\"], DatabaseIncludeSpecialColumns>,\n // Check if select was applied: if Selected is Record (object select) or a subset of keys, select was applied\n IsSingleField extends true\n ? false // Single field operations don't include special columns\n : // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n Selected extends Record<string, Column<any, any, any>>\n ? true\n : Selected extends keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n ? false\n : true\n >,\n EO[\"includeODataAnnotations\"] extends true ? true : false\n >\n >\n > {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Normal record: /tableName('recordId') - use FMTID if configured\n const tableId = this.getTableId(options?.useEntityIds ?? this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n const mergedOptions = this.mergeExecuteOptions(options);\n\n if (this.operation === \"getSingleField\" && this.operationParam) {\n url += `/${this.operationParam}`;\n } else {\n // Add query string for select/expand (only when not getting a single field)\n const queryString = this.buildQueryString(mergedOptions.includeSpecialColumns, mergedOptions.useEntityIds);\n url += queryString;\n }\n const result = await this.context._makeRequest(url, mergedOptions);\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n // The type is extracted from the Column stored in FieldColumn generic\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n const fieldResponse = response as ODataFieldResponse<any>;\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type extraction\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);\n\n return processODataResponse(response, {\n table: this.table,\n schema: getSchemaFromTable(this.table),\n singleMode: \"exact\",\n selectedFields: this.selectedFields,\n expandValidationConfigs,\n skipValidation: options?.skipValidation,\n useEntityIds: mergedOptions.useEntityIds,\n includeSpecialColumns: mergedOptions.includeSpecialColumns,\n fieldMapping: this.fieldMapping,\n });\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value\n getRequestConfig(): { method: string; url: string; body?: any } {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // For batch operations, use database-level setting (no per-request override available here)\n const tableId = this.getTableId(this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n // Use the column's getFieldIdentifier to support entity IDs\n url += `/${this.operationColumn.getFieldIdentifier(this.databaseUseEntityIds)}`;\n } else if (this.operation === \"getSingleField\" && this.operationParam) {\n // Fallback for backwards compatibility (shouldn't happen in normal flow)\n url += `/${this.operationParam}`;\n } else {\n // Add query string for select/expand (only when not getting a single field)\n const queryString = this.buildQueryString();\n url += queryString;\n }\n\n return {\n method: \"GET\",\n url,\n };\n }\n\n /**\n * Returns the query string for this record builder (for testing purposes).\n */\n getQueryString(options?: { useEntityIds?: boolean }): string {\n const useEntityIds = options?.useEntityIds ?? this.databaseUseEntityIds;\n let path: string;\n\n // Build the path depending on navigation context\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n path = `/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Use getTableId to respect entity ID settings (same as getRequestConfig)\n const tableId = this.getTableId(useEntityIds);\n path = `/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n return `${path}/${this.operationColumn.getFieldIdentifier(useEntityIds)}`;\n }\n if (this.operation === \"getSingleField\" && this.operationParam) {\n // Fallback for backwards compatibility (shouldn't happen in normal flow)\n return `${path}/${this.operationParam}`;\n }\n\n const queryString = this.buildQueryString(undefined, useEntityIds);\n return `${path}${queryString}`;\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): Request {\n const config = this.getRequestConfig();\n return createODataRequest(baseUrl, config, options);\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >\n >\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = this.table ? getTableName(this.table) : \"unknown\";\n const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);\n return { data: undefined, error };\n }\n\n // Use safeJsonParse to handle FileMaker's invalid JSON with unquoted ? values\n const rawResponse = await safeJsonParse(response);\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n // The type is extracted from the Column stored in FieldColumn generic\n // biome-ignore lint/suspicious/noExplicitAny: Type parameter inferred from FieldColumn generic\n const fieldResponse = rawResponse as ODataFieldResponse<any>;\n // biome-ignore lint/suspicious/noExplicitAny: Type parameter inferred from FieldColumn generic\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const mergedOptions = this.mergeExecuteOptions(options);\n const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);\n\n return processODataResponse(rawResponse, {\n table: this.table,\n schema: getSchemaFromTable(this.table),\n singleMode: \"exact\",\n selectedFields: this.selectedFields,\n expandValidationConfigs,\n skipValidation: options?.skipValidation,\n useEntityIds: mergedOptions.useEntityIds,\n includeSpecialColumns: mergedOptions.includeSpecialColumns,\n fieldMapping: this.fieldMapping,\n });\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"record-builder.js","sources":["../../../src/client/record-builder.ts"],"sourcesContent":["/** biome-ignore-all lint/complexity/noBannedTypes: Empty object type represents no expands by default */\nimport type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport { createLogger, type InternalLogger } from \"../logger\";\nimport type { Column } from \"../orm/column\";\nimport type { ExtractTableName, FMTable, InferSchemaOutputFromFMTable, ValidExpandTarget } from \"../orm/table\";\nimport { getNavigationPaths, getTableName } from \"../orm/table\";\nimport type {\n ConditionallyWithODataAnnotations,\n ConditionallyWithSpecialColumns,\n ExecutableBuilder,\n ExecuteMethodOptions,\n ExecuteOptions,\n ExecutionContext,\n NormalizeIncludeSpecialColumns,\n ODataFieldResponse,\n Result,\n} from \"../types\";\nimport {\n buildSelectExpandQueryString,\n createODataRequest,\n ExpandBuilder,\n type ExpandConfig,\n type ExpandedRelations,\n getSchemaFromTable,\n mergeExecuteOptions,\n processODataResponse,\n processSelectWithRenames,\n resolveTableId,\n} from \"./builders/index\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport type { ResolveExpandedRelations, SystemColumnsFromOption, SystemColumnsOption } from \"./query/types\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { safeJsonParse } from \"./sanitize-json\";\n\n/**\n * Extract the value type from a Column.\n * This uses the phantom type stored in Column to get the actual value type.\n */\n// biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer\ntype ExtractColumnType<C> = C extends Column<infer T, any> ? T : never;\n\n/**\n * Map a select object to its return type.\n * For each key in the select object, extract the type from the corresponding Column.\n */\ntype MapSelectToReturnType<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n TSelect extends Record<string, Column<any, any, any, any>>,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape\n _TSchema extends Record<string, any>,\n> = {\n [K in keyof TSelect]: ExtractColumnType<TSelect[K]>;\n};\n\n// Return type for RecordBuilder execute\nexport type RecordReturnType<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape\n Schema extends Record<string, any>,\n IsSingleField extends boolean,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n FieldColumn extends Column<any, any, any, any> | undefined,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration, accepts any FMTable\n Selected extends keyof Schema | Record<string, Column<any, any, ExtractTableName<FMTable<any, any>>>>,\n Expands extends ExpandedRelations,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> = IsSingleField extends true\n ? // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer\n FieldColumn extends Column<infer TOutput, any, any, any>\n ? TOutput\n : never\n : // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n [Selected] extends [Record<string, Column<any, any, any, any>>]\n ? MapSelectToReturnType<Selected, Schema> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>\n : // Use tuple wrapping to prevent distribution over union of keys\n [Selected] extends [keyof Schema]\n ? Pick<Schema, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>\n : never;\n\nexport class RecordBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration, default allows untyped tables\n Occ extends FMTable<any, any> = FMTable<any, any>,\n IsSingleField extends boolean = false,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n FieldColumn extends Column<any, any, any, any> | undefined = undefined,\n Selected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = keyof InferSchemaOutputFromFMTable<\n NonNullable<Occ>\n >,\n Expands extends ExpandedRelations = {},\n DatabaseIncludeSpecialColumns extends boolean = false,\n SystemCols extends SystemColumnsOption | undefined = undefined,\n> implements\n ExecutableBuilder<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >\n >\n{\n private readonly table: Occ;\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly recordId: string | number;\n private readonly operation?: \"getSingleField\" | \"navigate\";\n private readonly operationParam?: string;\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n private readonly operationColumn?: Column<any, any, any, any>;\n private readonly isNavigateFromEntitySet?: boolean;\n private readonly navigateRelation?: string;\n private readonly navigateSourceTableName?: string;\n\n private readonly databaseUseEntityIds: boolean;\n private readonly databaseIncludeSpecialColumns: boolean;\n\n // Properties for select/expand support\n private readonly selectedFields?: string[];\n private readonly expandConfigs: ExpandConfig[] = [];\n // Mapping from field names to output keys (for renamed fields in select)\n private readonly fieldMapping?: Record<string, string>;\n // System columns requested via select() second argument\n private readonly systemColumns?: SystemColumnsOption;\n\n private readonly logger: InternalLogger;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n recordId: string | number;\n databaseUseEntityIds?: boolean;\n databaseIncludeSpecialColumns?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.recordId = config.recordId;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;\n this.logger = config.context?._getLogger?.() ?? createLogger();\n }\n\n /**\n * Helper to merge database-level useEntityIds and includeSpecialColumns with per-request options\n */\n private mergeExecuteOptions(options?: RequestInit & FFetchOptions & ExecuteOptions): RequestInit &\n FFetchOptions & {\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n } {\n const merged = mergeExecuteOptions(options, this.databaseUseEntityIds);\n return {\n ...merged,\n includeSpecialColumns: options?.includeSpecialColumns ?? this.databaseIncludeSpecialColumns,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n if (!this.table) {\n throw new Error(\"Table occurrence is required\");\n }\n return resolveTableId(this.table, getTableName(this.table), this.context, useEntityIds);\n }\n\n /**\n * Creates a new RecordBuilder with modified configuration.\n * Used by select() to create new instances.\n */\n private cloneWithChanges<\n NewSelected extends\n | keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n | Record<string, Column<any, any, ExtractTableName<NonNullable<Occ>>>> = Selected,\n NewSystemCols extends SystemColumnsOption | undefined = SystemCols,\n >(changes: {\n selectedFields?: string[];\n fieldMapping?: Record<string, string>;\n systemColumns?: NewSystemCols;\n }): RecordBuilder<Occ, false, FieldColumn, NewSelected, Expands, DatabaseIncludeSpecialColumns, NewSystemCols> {\n const newBuilder = new RecordBuilder<\n Occ,\n false,\n FieldColumn,\n NewSelected,\n Expands,\n DatabaseIncludeSpecialColumns,\n NewSystemCols\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n // Use type assertion to allow assignment to readonly properties on new instance\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n mutableBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;\n mutableBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;\n mutableBuilder.systemColumns = changes.systemColumns !== undefined ? changes.systemColumns : this.systemColumns;\n mutableBuilder.expandConfigs = [...this.expandConfigs];\n // Preserve navigation context\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n mutableBuilder.operationColumn = this.operationColumn;\n return newBuilder;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n getSingleField<TColumn extends Column<any, any, ExtractTableName<NonNullable<Occ>>, any>>(\n column: TColumn,\n ): RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {},\n DatabaseIncludeSpecialColumns\n > {\n // Runtime validation: ensure column is from the correct table\n const tableName = getTableName(this.table);\n if (!column.isFromTable(tableName)) {\n throw new Error(`Column ${column.toString()} is not from table ${tableName}`);\n }\n\n const newBuilder = new RecordBuilder<\n Occ,\n true,\n TColumn,\n keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n {},\n DatabaseIncludeSpecialColumns\n >({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n // Use type assertion to allow assignment to readonly properties on new instance\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n mutableBuilder.operation = \"getSingleField\";\n mutableBuilder.operationColumn = column;\n mutableBuilder.operationParam = column.getFieldIdentifier(this.databaseUseEntityIds);\n // Preserve navigation context\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n return newBuilder;\n }\n\n /**\n * Select fields using column references.\n * Allows renaming fields by using different keys in the object.\n * Container fields cannot be selected and will cause a type error.\n *\n * @example\n * db.from(contacts).get(\"uuid\").select({\n * name: contacts.name,\n * userEmail: contacts.email // renamed!\n * })\n *\n * @example\n * // Include system columns (ROWID, ROWMODID) when using select()\n * db.from(contacts).get(\"uuid\").select(\n * { name: contacts.name },\n * { ROWID: true, ROWMODID: true }\n * )\n *\n * @param fields - Object mapping output keys to column references (container fields excluded)\n * @param systemColumns - Optional object to request system columns (ROWID, ROWMODID)\n * @returns RecordBuilder with updated selected fields\n */\n select<\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n TSelect extends Record<string, Column<any, any, ExtractTableName<Occ>, false>>,\n TSystemCols extends SystemColumnsOption = {},\n >(\n fields: TSelect,\n systemColumns?: TSystemCols,\n ): RecordBuilder<Occ, false, FieldColumn, TSelect, Expands, DatabaseIncludeSpecialColumns, TSystemCols> {\n const tableName = getTableName(this.table);\n const { selectedFields, fieldMapping } = processSelectWithRenames(fields, tableName, this.logger);\n\n // Add system columns to selectedFields if requested\n const finalSelectedFields = [...selectedFields];\n if (systemColumns?.ROWID) {\n finalSelectedFields.push(\"ROWID\");\n }\n if (systemColumns?.ROWMODID) {\n finalSelectedFields.push(\"ROWMODID\");\n }\n\n return this.cloneWithChanges({\n selectedFields: finalSelectedFields,\n fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : undefined,\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type parameter\n systemColumns: systemColumns as any,\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for complex generic return type\n }) as any;\n }\n\n /**\n * Expand a navigation property to include related records.\n * Supports nested select, filter, orderBy, and expand operations.\n *\n * @example\n * ```typescript\n * // Simple expand with FMTable object\n * const contact = await db.from(contacts).get(\"uuid\").expand(users).execute();\n *\n * // Expand with select\n * const contact = await db.from(contacts).get(\"uuid\")\n * .expand(users, b => b.select({ username: users.username, email: users.email }))\n * .execute();\n * ```\n */\n expand<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n TargetTable extends FMTable<any, any>,\n TSelected extends\n | keyof InferSchemaOutputFromFMTable<TargetTable>\n | Record<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n Column<any, any, ExtractTableName<TargetTable>>\n > = keyof InferSchemaOutputFromFMTable<TargetTable>,\n TNestedExpands extends ExpandedRelations = {},\n >(\n targetTable: ValidExpandTarget<Occ, TargetTable>,\n callback?: (\n builder: QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}>,\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n ) => QueryBuilder<TargetTable, TSelected, any, any, TNestedExpands>,\n ): RecordBuilder<\n Occ,\n false,\n FieldColumn,\n Selected,\n Expands & {\n [K in ExtractTableName<TargetTable>]: {\n schema: InferSchemaOutputFromFMTable<TargetTable>;\n selected: TSelected;\n nested: TNestedExpands;\n };\n },\n DatabaseIncludeSpecialColumns,\n SystemCols\n > {\n // Create new builder with updated types\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any ExpandedRelations\n const newBuilder = new RecordBuilder<Occ, false, FieldColumn, Selected, any, DatabaseIncludeSpecialColumns>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n recordId: this.recordId,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n\n // Use type assertion to allow assignment to readonly properties on new instance\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n const mutableBuilder = newBuilder as any;\n // Copy existing state\n mutableBuilder.selectedFields = this.selectedFields;\n mutableBuilder.fieldMapping = this.fieldMapping;\n mutableBuilder.systemColumns = this.systemColumns;\n mutableBuilder.expandConfigs = [...this.expandConfigs];\n mutableBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;\n mutableBuilder.navigateRelation = this.navigateRelation;\n mutableBuilder.navigateSourceTableName = this.navigateSourceTableName;\n mutableBuilder.operationColumn = this.operationColumn;\n\n // Use ExpandBuilder.processExpand to handle the expand logic\n const expandBuilder = new ExpandBuilder(this.databaseUseEntityIds, this.logger);\n type TargetBuilder = QueryBuilder<TargetTable, keyof InferSchemaOutputFromFMTable<TargetTable>, false, false, {}>;\n const expandConfig = expandBuilder.processExpand<TargetTable, TargetBuilder>(\n targetTable,\n this.table ?? undefined,\n callback as ((builder: TargetBuilder) => TargetBuilder) | undefined,\n () =>\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n new QueryBuilder<TargetTable, any, any, any, any, DatabaseIncludeSpecialColumns, undefined>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n }),\n );\n\n mutableBuilder.expandConfigs.push(expandConfig);\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion needed as expand changes generic parameters in complex way that TypeScript cannot infer\n return newBuilder as any;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n navigate<TargetTable extends FMTable<any, any>>(\n targetTable: ValidExpandTarget<Occ, TargetTable>,\n ): QueryBuilder<\n TargetTable,\n keyof InferSchemaOutputFromFMTable<TargetTable>,\n false,\n false,\n {},\n DatabaseIncludeSpecialColumns,\n undefined\n > {\n // Extract name and validate\n const relationName = getTableName(targetTable);\n\n // Runtime validation: Check if relation name is in navigationPaths\n if (this.table) {\n const navigationPaths = getNavigationPaths(this.table);\n if (navigationPaths && !navigationPaths.includes(relationName)) {\n this.logger.warn(\n `Cannot navigate to \"${relationName}\". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(\", \") : \"none\"}`,\n );\n }\n }\n\n // Create QueryBuilder with target table\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any QueryBuilder configuration\n const builder = new QueryBuilder<TargetTable, any, any, any, any, DatabaseIncludeSpecialColumns, undefined>({\n occurrence: targetTable,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.databaseUseEntityIds,\n databaseIncludeSpecialColumns: this.databaseIncludeSpecialColumns,\n });\n\n // Store the navigation info - we'll use it in execute\n // Use relation name as-is (entity ID handling is done in QueryBuilder)\n const relationId = relationName;\n\n // If this RecordBuilder came from a navigated EntitySet, we need to preserve that base path\n let sourceTableName: string;\n let baseRelation: string | undefined;\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // Build the base path: /sourceTable/relation('recordId')/newRelation\n sourceTableName = this.navigateSourceTableName;\n baseRelation = this.navigateRelation;\n } else {\n // Normal record navigation: /tableName('recordId')/relation\n // Use table ID if available, otherwise table name\n if (!this.table) {\n throw new Error(\"Table occurrence is required for navigation\");\n }\n sourceTableName = resolveTableId(this.table, getTableName(this.table), this.context, this.databaseUseEntityIds);\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n (builder as any).navigation = {\n recordId: this.recordId,\n relation: relationId,\n sourceTableName,\n baseRelation,\n };\n // biome-ignore lint/suspicious/noExplicitAny: Mutation of readonly properties for builder pattern\n (builder as any).navigation = {\n recordId: this.recordId,\n relation: relationId,\n sourceTableName,\n baseRelation,\n };\n\n return builder;\n }\n\n /**\n * Builds the complete query string including $select and $expand parameters.\n */\n private buildQueryString(includeSpecialColumns?: boolean, useEntityIds?: boolean): string {\n // Use merged includeSpecialColumns if provided, otherwise use database-level default\n const finalIncludeSpecialColumns = includeSpecialColumns ?? this.databaseIncludeSpecialColumns;\n // Use merged useEntityIds if provided, otherwise use database-level default\n const finalUseEntityIds = useEntityIds ?? this.databaseUseEntityIds;\n\n return buildSelectExpandQueryString({\n selectedFields: this.selectedFields,\n expandConfigs: this.expandConfigs,\n table: this.table,\n useEntityIds: finalUseEntityIds,\n logger: this.logger,\n includeSpecialColumns: finalIncludeSpecialColumns,\n });\n }\n\n async execute<EO extends ExecuteOptions>(\n options?: ExecuteMethodOptions<EO>,\n ): Promise<\n Result<\n ConditionallyWithODataAnnotations<\n ConditionallyWithSpecialColumns<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >,\n // Use the merged value: if explicitly provided in options, use that; otherwise use database default\n NormalizeIncludeSpecialColumns<EO[\"includeSpecialColumns\"], DatabaseIncludeSpecialColumns>,\n // Check if select was applied: if Selected is Record (object select) or a subset of keys, select was applied\n IsSingleField extends true\n ? false // Single field operations don't include special columns\n : // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n Selected extends Record<string, Column<any, any, any>>\n ? true\n : Selected extends keyof InferSchemaOutputFromFMTable<NonNullable<Occ>>\n ? false\n : true\n >,\n EO[\"includeODataAnnotations\"] extends true ? true : false\n >\n >\n > {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Normal record: /tableName('recordId') - use FMTID if configured\n const tableId = this.getTableId(options?.useEntityIds ?? this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n const mergedOptions = this.mergeExecuteOptions(options);\n\n if (this.operation === \"getSingleField\" && this.operationParam) {\n url += `/${this.operationParam}`;\n } else {\n // Add query string for select/expand (only when not getting a single field)\n const queryString = this.buildQueryString(mergedOptions.includeSpecialColumns, mergedOptions.useEntityIds);\n url += queryString;\n }\n const result = await this.context._makeRequest(url, mergedOptions);\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n // The type is extracted from the Column stored in FieldColumn generic\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n const fieldResponse = response as ODataFieldResponse<any>;\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type extraction\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);\n\n return processODataResponse(response, {\n table: this.table,\n schema: getSchemaFromTable(this.table),\n singleMode: \"exact\",\n selectedFields: this.selectedFields,\n expandValidationConfigs,\n skipValidation: options?.skipValidation,\n useEntityIds: mergedOptions.useEntityIds,\n includeSpecialColumns: mergedOptions.includeSpecialColumns,\n fieldMapping: this.fieldMapping,\n });\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value\n getRequestConfig(): { method: string; url: string; body?: any } {\n let url: string;\n\n // Build the base URL depending on whether this came from a navigated EntitySet\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n // From navigated EntitySet: /sourceTable/relation('recordId')\n url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // For batch operations, use database-level setting (no per-request override available here)\n const tableId = this.getTableId(this.databaseUseEntityIds);\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n // Use the column's getFieldIdentifier to support entity IDs\n url += `/${this.operationColumn.getFieldIdentifier(this.databaseUseEntityIds)}`;\n } else if (this.operation === \"getSingleField\" && this.operationParam) {\n // Fallback for backwards compatibility (shouldn't happen in normal flow)\n url += `/${this.operationParam}`;\n } else {\n // Add query string for select/expand (only when not getting a single field)\n const queryString = this.buildQueryString();\n url += queryString;\n }\n\n return {\n method: \"GET\",\n url,\n };\n }\n\n /**\n * Returns the query string for this record builder (for testing purposes).\n */\n getQueryString(options?: { useEntityIds?: boolean }): string {\n const useEntityIds = options?.useEntityIds ?? this.databaseUseEntityIds;\n let path: string;\n\n // Build the path depending on navigation context\n if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {\n path = `/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;\n } else {\n // Use getTableId to respect entity ID settings (same as getRequestConfig)\n const tableId = this.getTableId(useEntityIds);\n path = `/${tableId}('${this.recordId}')`;\n }\n\n if (this.operation === \"getSingleField\" && this.operationColumn) {\n return `${path}/${this.operationColumn.getFieldIdentifier(useEntityIds)}`;\n }\n if (this.operation === \"getSingleField\" && this.operationParam) {\n // Fallback for backwards compatibility (shouldn't happen in normal flow)\n return `${path}/${this.operationParam}`;\n }\n\n const queryString = this.buildQueryString(undefined, useEntityIds);\n return `${path}${queryString}`;\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): Request {\n const config = this.getRequestConfig();\n return createODataRequest(baseUrl, config, options);\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<\n Result<\n RecordReturnType<\n InferSchemaOutputFromFMTable<NonNullable<Occ>>,\n IsSingleField,\n FieldColumn,\n Selected,\n Expands,\n SystemCols\n >\n >\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = this.table ? getTableName(this.table) : \"unknown\";\n const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);\n return { data: undefined, error };\n }\n\n // Use safeJsonParse to handle FileMaker's invalid JSON with unquoted ? values\n const rawResponse = await safeJsonParse(response);\n\n // Handle single field operation\n if (this.operation === \"getSingleField\") {\n // Single field returns a JSON object with @context and value\n // The type is extracted from the Column stored in FieldColumn generic\n // biome-ignore lint/suspicious/noExplicitAny: Type parameter inferred from FieldColumn generic\n const fieldResponse = rawResponse as ODataFieldResponse<any>;\n // biome-ignore lint/suspicious/noExplicitAny: Type parameter inferred from FieldColumn generic\n return { data: fieldResponse.value as any, error: undefined };\n }\n\n // Use shared response processor\n const mergedOptions = this.mergeExecuteOptions(options);\n const expandBuilder = new ExpandBuilder(mergedOptions.useEntityIds ?? false, this.logger);\n const expandValidationConfigs = expandBuilder.buildValidationConfigs(this.expandConfigs);\n\n return processODataResponse(rawResponse, {\n table: this.table,\n schema: getSchemaFromTable(this.table),\n singleMode: \"exact\",\n selectedFields: this.selectedFields,\n expandValidationConfigs,\n skipValidation: options?.skipValidation,\n useEntityIds: mergedOptions.useEntityIds,\n includeSpecialColumns: mergedOptions.includeSpecialColumns,\n fieldMapping: this.fieldMapping,\n });\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize-json.js","sources":["../../../src/client/sanitize-json.ts"],"sourcesContent":["/**\n * FileMaker OData API sometimes returns invalid JSON containing unquoted `?`\n * characters as field values (e.g., `\"fieldName\": ?`), which causes JSON.parse()\n * to fail. This module provides utilities to sanitize such responses before parsing.\n */\n\nimport { ResponseParseError } from \"../errors\";\n\n/**\n * Sanitizes FileMaker OData JSON responses by replacing unquoted `?` values with `null`.\n *\n * FileMaker uses `?` to represent undefined/null values in its OData responses,\n * but this is not valid JSON. This function converts those to proper `null` values.\n *\n * The regex uses two patterns:\n * 1. `/:\\s*\\?(?=\\s*[,}\\]])/g` - for values in objects (after `:`)\n * 2. `/(?<=[\\[,])\\s*\\?(?=\\s*[,\\]])/g` - for values in arrays (after `[` or `,`)\n *\n * @param text - The raw response text from FileMaker OData API\n * @returns Sanitized JSON string with `?` values replaced by `null`\n *\n * @example\n * sanitizeFileMakerJson('{\"field1\": \"valid\", \"field2\": ?, \"field3\": null}')\n * // Returns: '{\"field1\": \"valid\", \"field2\": null, \"field3\": null}'\n */\nexport function sanitizeFileMakerJson(text: string): string {\n // Replace unquoted ? values in objects (after colon)\n // Also handles arrays when the array is a value in an object\n let result = text.replace(/:\\s*\\?(?=\\s*[,}\\]])/g, \": null\");\n\n // Replace unquoted ? values directly in arrays (not after colon)\n // e.g., [1, ?, 3] -> [1, null, 3]\n result = result.replace(/(?<=[[,])\\s*\\?(?=\\s*[,\\]])/g, \" null\");\n\n return result;\n}\n\n/**\n * Safely parses a Response body as JSON, handling FileMaker's invalid JSON responses.\n *\n * This function reads the response as text first, sanitizes any invalid `?` values,\n * and then parses the sanitized JSON. This approach handles the case where FileMaker\n * returns a Content-Type of application/json but the body contains invalid JSON.\n *\n * @param response - The fetch Response object\n * @returns Parsed JSON data\n * @throws ResponseParseError if the JSON is still invalid after sanitization (includes sanitized text for debugging)\n */\nexport async function safeJsonParse<T = unknown>(response: Response): Promise<T> {\n const text = await response.text();\n const sanitized = sanitizeFileMakerJson(text);\n try {\n return JSON.parse(sanitized) as T;\n } catch (err) {\n throw new ResponseParseError(\n response.url,\n `Failed to parse response as JSON: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n {\n rawText: sanitized,\n cause: err instanceof Error ? err : undefined,\n },\n );\n }\n}\n"],"names":[],"mappings":";AAyBO,SAAS,sBAAsB,MAAsB;AAG1D,MAAI,SAAS,KAAK,QAAQ,wBAAwB,QAAQ;AAIjD,WAAA,OAAO,QAAQ,WAAA,kCAAA,GAAA,GAA+B,OAAO;AAEvD,SAAA;AACT;AAaA,eAAsB,cAA2B,UAAgC;AACzE,QAAA,OAAO,MAAM,SAAS,KAAK;AAC3B,QAAA,YAAY,sBAAsB,IAAI;AACxC,MAAA;AACK,WAAA,KAAK,MAAM,SAAS;AAAA,WACpB,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,SAAS;AAAA,MACT,qCAAqC,eAAe,QAAQ,IAAI,UAAU,eAAe;AAAA,MACzF;AAAA,QACE,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,MAAM;AAAA,MAAA;AAAA,IAExC;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"sanitize-json.js","sources":["../../../src/client/sanitize-json.ts"],"sourcesContent":["/**\n * FileMaker OData API sometimes returns invalid JSON containing unquoted `?`\n * characters as field values (e.g., `\"fieldName\": ?`), which causes JSON.parse()\n * to fail. This module provides utilities to sanitize such responses before parsing.\n */\n\nimport { ResponseParseError } from \"../errors\";\n\n/**\n * Sanitizes FileMaker OData JSON responses by replacing unquoted `?` values with `null`.\n *\n * FileMaker uses `?` to represent undefined/null values in its OData responses,\n * but this is not valid JSON. This function converts those to proper `null` values.\n *\n * The regex uses two patterns:\n * 1. `/:\\s*\\?(?=\\s*[,}\\]])/g` - for values in objects (after `:`)\n * 2. `/(?<=[\\[,])\\s*\\?(?=\\s*[,\\]])/g` - for values in arrays (after `[` or `,`)\n *\n * @param text - The raw response text from FileMaker OData API\n * @returns Sanitized JSON string with `?` values replaced by `null`\n *\n * @example\n * sanitizeFileMakerJson('{\"field1\": \"valid\", \"field2\": ?, \"field3\": null}')\n * // Returns: '{\"field1\": \"valid\", \"field2\": null, \"field3\": null}'\n */\nexport function sanitizeFileMakerJson(text: string): string {\n // Replace unquoted ? values in objects (after colon)\n // Also handles arrays when the array is a value in an object\n let result = text.replace(/:\\s*\\?(?=\\s*[,}\\]])/g, \": null\");\n\n // Replace unquoted ? values directly in arrays (not after colon)\n // e.g., [1, ?, 3] -> [1, null, 3]\n result = result.replace(/(?<=[[,])\\s*\\?(?=\\s*[,\\]])/g, \" null\");\n\n return result;\n}\n\n/**\n * Safely parses a Response body as JSON, handling FileMaker's invalid JSON responses.\n *\n * This function reads the response as text first, sanitizes any invalid `?` values,\n * and then parses the sanitized JSON. This approach handles the case where FileMaker\n * returns a Content-Type of application/json but the body contains invalid JSON.\n *\n * @param response - The fetch Response object\n * @returns Parsed JSON data\n * @throws ResponseParseError if the JSON is still invalid after sanitization (includes sanitized text for debugging)\n */\nexport async function safeJsonParse<T = unknown>(response: Response): Promise<T> {\n const text = await response.text();\n const sanitized = sanitizeFileMakerJson(text);\n try {\n return JSON.parse(sanitized) as T;\n } catch (err) {\n throw new ResponseParseError(\n response.url,\n `Failed to parse response as JSON: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n {\n rawText: sanitized,\n cause: err instanceof Error ? err : undefined,\n },\n );\n }\n}\n"],"names":[],"mappings":";AAyBO,SAAS,sBAAsB,MAAsB;AAG1D,MAAI,SAAS,KAAK,QAAQ,wBAAwB,QAAQ;AAI1D,WAAS,OAAO,QAAQ,WAAA,kCAAA,GAAA,GAA+B,OAAO;AAE9D,SAAO;AACT;AAaA,eAAsB,cAA2B,UAAgC;AAC/E,QAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,QAAM,YAAY,sBAAsB,IAAI;AAC5C,MAAI;AACF,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,SAAS;AAAA,MACT,qCAAqC,eAAe,QAAQ,IAAI,UAAU,eAAe;AAAA,MACzF;AAAA,QACE,SAAS;AAAA,QACT,OAAO,eAAe,QAAQ,MAAM;AAAA,MAAA;AAAA,IACtC;AAAA,EAEJ;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema-manager.js","sources":["../../../src/client/schema-manager.ts"],"sourcesContent":["import type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { ExecutionContext } from \"../types\";\n\ninterface GenericField {\n name: string;\n nullable?: boolean;\n primary?: boolean;\n unique?: boolean;\n global?: boolean;\n repetitions?: number;\n}\n\ntype StringField = GenericField & {\n type: \"string\";\n maxLength?: number;\n default?: \"USER\" | \"USERNAME\" | \"CURRENT_USER\";\n};\n\ntype NumericField = GenericField & {\n type: \"numeric\";\n};\n\ntype DateField = GenericField & {\n type: \"date\";\n default?: \"CURRENT_DATE\" | \"CURDATE\";\n};\n\ntype TimeField = GenericField & {\n type: \"time\";\n default?: \"CURRENT_TIME\" | \"CURTIME\";\n};\n\ntype TimestampField = GenericField & {\n type: \"timestamp\";\n default?: \"CURRENT_TIMESTAMP\" | \"CURTIMESTAMP\";\n};\n\ntype ContainerField = GenericField & {\n type: \"container\";\n externalSecurePath?: string;\n};\n\nexport type Field = StringField | NumericField | DateField | TimeField | TimestampField | ContainerField;\n\nexport type { StringField, NumericField, DateField, TimeField, TimestampField, ContainerField };\n\ntype FileMakerField = Omit<Field, \"type\" | \"repetitions\" | \"maxLength\"> & {\n type: string;\n};\n\ninterface TableDefinition {\n tableName: string;\n fields: FileMakerField[];\n}\n\nexport class SchemaManager {\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n\n constructor(databaseName: string, context: ExecutionContext) {\n this.databaseName = databaseName;\n this.context = context;\n }\n\n async createTable(\n tableName: string,\n fields: Field[],\n options?: RequestInit & FFetchOptions,\n ): Promise<TableDefinition> {\n const result = await this.context._makeRequest<TableDefinition>(`/${this.databaseName}/FileMaker_Tables`, {\n method: \"POST\",\n body: JSON.stringify({\n tableName,\n fields: fields.map(SchemaManager.compileFieldDefinition),\n }),\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n async addFields(tableName: string, fields: Field[], options?: RequestInit & FFetchOptions): Promise<TableDefinition> {\n const result = await this.context._makeRequest<TableDefinition>(\n `/${this.databaseName}/FileMaker_Tables/${tableName}`,\n {\n method: \"PATCH\",\n body: JSON.stringify({\n fields: fields.map(SchemaManager.compileFieldDefinition),\n }),\n ...options,\n },\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n async deleteTable(tableName: string, options?: RequestInit & FFetchOptions): Promise<void> {\n const result = await this.context._makeRequest(`/${this.databaseName}/FileMaker_Tables/${tableName}`, {\n method: \"DELETE\",\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n }\n\n async deleteField(tableName: string, fieldName: string, options?: RequestInit & FFetchOptions): Promise<void> {\n const result = await this.context._makeRequest(`/${this.databaseName}/FileMaker_Tables/${tableName}/${fieldName}`, {\n method: \"DELETE\",\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n }\n\n async createIndex(\n tableName: string,\n fieldName: string,\n options?: RequestInit & FFetchOptions,\n ): Promise<{ indexName: string }> {\n const result = await this.context._makeRequest<{ indexName: string }>(\n `/${this.databaseName}/FileMaker_Indexes/${tableName}`,\n {\n method: \"POST\",\n body: JSON.stringify({ indexName: fieldName }),\n ...options,\n },\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n async deleteIndex(tableName: string, fieldName: string, options?: RequestInit & FFetchOptions): Promise<void> {\n const result = await this.context._makeRequest(\n `/${this.databaseName}/FileMaker_Indexes/${tableName}/${fieldName}`,\n {\n method: \"DELETE\",\n ...options,\n },\n );\n\n if (result.error) {\n throw result.error;\n }\n }\n\n private static compileFieldDefinition(field: Field): FileMakerField {\n let type: string = field.type;\n const repetitions = field.repetitions;\n\n // Handle string fields - convert to varchar and add maxLength if present\n if (field.type === \"string\") {\n type = \"varchar\";\n const stringField = field as StringField;\n if (stringField.maxLength !== undefined) {\n type += `(${stringField.maxLength})`;\n }\n }\n\n // Add repetitions suffix if present\n if (repetitions !== undefined) {\n type += `[${repetitions}]`;\n }\n\n // Build the result object, excluding type, maxLength, and repetitions\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic result object construction\n const result: any = {\n name: field.name,\n type,\n };\n\n // Add optional properties that FileMaker expects\n if (field.nullable !== undefined) {\n result.nullable = field.nullable;\n }\n if (field.primary !== undefined) {\n result.primary = field.primary;\n }\n if (field.unique !== undefined) {\n result.unique = field.unique;\n }\n if (field.global !== undefined) {\n result.global = field.global;\n }\n\n // Add type-specific properties\n if (field.type === \"string\") {\n const stringField = field as StringField;\n if (stringField.default !== undefined) {\n result.default = stringField.default;\n }\n } else if (field.type === \"date\") {\n const dateField = field as DateField;\n if (dateField.default !== undefined) {\n result.default = dateField.default;\n }\n } else if (field.type === \"time\") {\n const timeField = field as TimeField;\n if (timeField.default !== undefined) {\n result.default = timeField.default;\n }\n } else if (field.type === \"timestamp\") {\n const timestampField = field as TimestampField;\n if (timestampField.default !== undefined) {\n result.default = timestampField.default;\n }\n } else if (field.type === \"container\") {\n const containerField = field as ContainerField;\n if (containerField.externalSecurePath !== undefined) {\n result.externalSecurePath = containerField.externalSecurePath;\n }\n }\n\n return result as FileMakerField;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-manager.js","sources":["../../../src/client/schema-manager.ts"],"sourcesContent":["import type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { ExecutionContext } from \"../types\";\n\ninterface GenericField {\n name: string;\n nullable?: boolean;\n primary?: boolean;\n unique?: boolean;\n global?: boolean;\n repetitions?: number;\n}\n\ntype StringField = GenericField & {\n type: \"string\";\n maxLength?: number;\n default?: \"USER\" | \"USERNAME\" | \"CURRENT_USER\";\n};\n\ntype NumericField = GenericField & {\n type: \"numeric\";\n};\n\ntype DateField = GenericField & {\n type: \"date\";\n default?: \"CURRENT_DATE\" | \"CURDATE\";\n};\n\ntype TimeField = GenericField & {\n type: \"time\";\n default?: \"CURRENT_TIME\" | \"CURTIME\";\n};\n\ntype TimestampField = GenericField & {\n type: \"timestamp\";\n default?: \"CURRENT_TIMESTAMP\" | \"CURTIMESTAMP\";\n};\n\ntype ContainerField = GenericField & {\n type: \"container\";\n externalSecurePath?: string;\n};\n\nexport type Field = StringField | NumericField | DateField | TimeField | TimestampField | ContainerField;\n\nexport type { StringField, NumericField, DateField, TimeField, TimestampField, ContainerField };\n\ntype FileMakerField = Omit<Field, \"type\" | \"repetitions\" | \"maxLength\"> & {\n type: string;\n};\n\ninterface TableDefinition {\n tableName: string;\n fields: FileMakerField[];\n}\n\nexport class SchemaManager {\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n\n constructor(databaseName: string, context: ExecutionContext) {\n this.databaseName = databaseName;\n this.context = context;\n }\n\n async createTable(\n tableName: string,\n fields: Field[],\n options?: RequestInit & FFetchOptions,\n ): Promise<TableDefinition> {\n const result = await this.context._makeRequest<TableDefinition>(`/${this.databaseName}/FileMaker_Tables`, {\n method: \"POST\",\n body: JSON.stringify({\n tableName,\n fields: fields.map(SchemaManager.compileFieldDefinition),\n }),\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n async addFields(tableName: string, fields: Field[], options?: RequestInit & FFetchOptions): Promise<TableDefinition> {\n const result = await this.context._makeRequest<TableDefinition>(\n `/${this.databaseName}/FileMaker_Tables/${tableName}`,\n {\n method: \"PATCH\",\n body: JSON.stringify({\n fields: fields.map(SchemaManager.compileFieldDefinition),\n }),\n ...options,\n },\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n async deleteTable(tableName: string, options?: RequestInit & FFetchOptions): Promise<void> {\n const result = await this.context._makeRequest(`/${this.databaseName}/FileMaker_Tables/${tableName}`, {\n method: \"DELETE\",\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n }\n\n async deleteField(tableName: string, fieldName: string, options?: RequestInit & FFetchOptions): Promise<void> {\n const result = await this.context._makeRequest(`/${this.databaseName}/FileMaker_Tables/${tableName}/${fieldName}`, {\n method: \"DELETE\",\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n }\n\n async createIndex(\n tableName: string,\n fieldName: string,\n options?: RequestInit & FFetchOptions,\n ): Promise<{ indexName: string }> {\n const result = await this.context._makeRequest<{ indexName: string }>(\n `/${this.databaseName}/FileMaker_Indexes/${tableName}`,\n {\n method: \"POST\",\n body: JSON.stringify({ indexName: fieldName }),\n ...options,\n },\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n async deleteIndex(tableName: string, fieldName: string, options?: RequestInit & FFetchOptions): Promise<void> {\n const result = await this.context._makeRequest(\n `/${this.databaseName}/FileMaker_Indexes/${tableName}/${fieldName}`,\n {\n method: \"DELETE\",\n ...options,\n },\n );\n\n if (result.error) {\n throw result.error;\n }\n }\n\n private static compileFieldDefinition(field: Field): FileMakerField {\n let type: string = field.type;\n const repetitions = field.repetitions;\n\n // Handle string fields - convert to varchar and add maxLength if present\n if (field.type === \"string\") {\n type = \"varchar\";\n const stringField = field as StringField;\n if (stringField.maxLength !== undefined) {\n type += `(${stringField.maxLength})`;\n }\n }\n\n // Add repetitions suffix if present\n if (repetitions !== undefined) {\n type += `[${repetitions}]`;\n }\n\n // Build the result object, excluding type, maxLength, and repetitions\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic result object construction\n const result: any = {\n name: field.name,\n type,\n };\n\n // Add optional properties that FileMaker expects\n if (field.nullable !== undefined) {\n result.nullable = field.nullable;\n }\n if (field.primary !== undefined) {\n result.primary = field.primary;\n }\n if (field.unique !== undefined) {\n result.unique = field.unique;\n }\n if (field.global !== undefined) {\n result.global = field.global;\n }\n\n // Add type-specific properties\n if (field.type === \"string\") {\n const stringField = field as StringField;\n if (stringField.default !== undefined) {\n result.default = stringField.default;\n }\n } else if (field.type === \"date\") {\n const dateField = field as DateField;\n if (dateField.default !== undefined) {\n result.default = dateField.default;\n }\n } else if (field.type === \"time\") {\n const timeField = field as TimeField;\n if (timeField.default !== undefined) {\n result.default = timeField.default;\n }\n } else if (field.type === \"timestamp\") {\n const timestampField = field as TimestampField;\n if (timestampField.default !== undefined) {\n result.default = timestampField.default;\n }\n } else if (field.type === \"container\") {\n const containerField = field as ContainerField;\n if (containerField.externalSecurePath !== undefined) {\n result.externalSecurePath = containerField.externalSecurePath;\n }\n }\n\n return result as FileMakerField;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { FMTable, InferSchemaOutputFromFMTable } from \"../orm/table\";\nimport { getBaseTableConfig, getTableId as getTableIdHelper, getTableName, isUsingEntityIds } from \"../orm/table\";\nimport { transformFieldNamesToIds } from \"../transform\";\nimport type { ExecutableBuilder, ExecuteMethodOptions, ExecuteOptions, ExecutionContext, Result } from \"../types\";\nimport { getAcceptHeader } from \"../types\";\nimport { validateAndTransformInput } from \"../validation\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport { QueryBuilder } from \"./query-builder\";\n\n/**\n * Initial update builder returned from EntitySet.update(data)\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class UpdateBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n Occ extends FMTable<any, any>,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> {\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly table: Occ;\n private readonly data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private readonly returnPreference: ReturnPreference;\n\n private readonly databaseUseEntityIds: boolean;\n private readonly databaseIncludeSpecialColumns: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n databaseIncludeSpecialColumns?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;\n }\n\n /**\n * Update a single record by ID\n * Returns updated count by default, or full record if returnFullRecord was set to true\n */\n byId(id: string | number): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns updated count by default, or full record if returnFullRecord was set to true\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(fn: (q: QueryBuilder<Occ>) => QueryBuilder<Occ>): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<Occ>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Let the user configure it\n const configuredBuilder = fn(queryBuilder);\n\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Can return either updated count or full record based on returnFullRecord option\n */\nexport class ExecutableUpdateBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n Occ extends FMTable<any, any>,\n _IsByFilter extends boolean,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> implements\n ExecutableBuilder<ReturnPreference extends \"minimal\" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>\n{\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly table: Occ;\n private readonly data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private readonly mode: \"byId\" | \"byFilter\";\n private readonly recordId?: string | number;\n private readonly queryBuilder?: QueryBuilder<Occ>;\n private readonly returnPreference: ReturnPreference;\n private readonly databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<Occ>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Helper to merge database-level useEntityIds with per-request options\n */\n private mergeExecuteOptions(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n // If useEntityIds is not set in options, use the database-level setting\n return {\n ...options,\n useEntityIds: options?.useEntityIds ?? this.databaseUseEntityIds,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n const contextDefault = this.context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIds ?? contextDefault;\n\n if (shouldUseIds) {\n if (!isUsingEntityIds(this.table)) {\n throw new Error(\n `useEntityIds is true but table \"${getTableName(this.table)}\" does not have entity IDs configured`,\n );\n }\n return getTableIdHelper(this.table);\n }\n\n return getTableName(this.table);\n }\n\n async execute(\n options?: ExecuteMethodOptions<ExecuteOptions>,\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>\n > {\n // Merge database-level useEntityIds with per-request options\n const mergedOptions = this.mergeExecuteOptions(options);\n\n // Get table identifier with override support\n const tableId = this.getTableId(mergedOptions.useEntityIds);\n\n // Validate and transform input data using input validators (writeValidators)\n let validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n\n try {\n validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n // If validation fails, return error immediately\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type\n } as any;\n }\n }\n\n // Transform field names to FMFIDs if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = mergedOptions.useEntityIds ?? false;\n\n const transformedData =\n this.table && shouldUseIds ? transformFieldNamesToIds(validatedData, this.table) : validatedData;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Update by filter: PATCH /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\n // The query string will have the tableId already transformed by QueryBuilder\n // Remove the leading \"/\" and table name from the query string as we'll build our own URL\n const tableName = getTableName(this.table);\n let queryParams: string;\n if (queryString.startsWith(`/${tableId}`)) {\n queryParams = queryString.slice(`/${tableId}`.length);\n } else if (queryString.startsWith(`/${tableName}`)) {\n queryParams = queryString.slice(`/${tableName}`.length);\n } else {\n queryParams = queryString;\n }\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Set Prefer header based on returnPreference\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.returnPreference === \"representation\") {\n headers.Prefer = \"return=representation\";\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers,\n body: JSON.stringify(transformedData),\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: response as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof response === \"number\") {\n updatedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n updatedCount = (response as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value\n getRequestConfig(): { method: string; url: string; body?: any } {\n // For batch operations, use database-level setting (no per-request override available here)\n // Note: Input validation happens in execute() and processResponse() for batch operations\n const tableId = this.getTableId(this.databaseUseEntityIds);\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData =\n this.table && this.databaseUseEntityIds ? transformFieldNamesToIds(this.data, this.table) : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n const queryString = this.queryBuilder.getQueryString();\n const tableName = getTableName(this.table);\n let queryParams: string;\n if (queryString.startsWith(`/${tableId}`)) {\n queryParams = queryString.slice(`/${tableId}`.length);\n } else if (queryString.startsWith(`/${tableName}`)) {\n queryParams = queryString.slice(`/${tableName}`.length);\n } else {\n queryParams = queryString;\n }\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\n body: JSON.stringify(transformedData),\n };\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): Request {\n const config = this.getRequestConfig();\n const fullUrl = `${baseUrl}${config.url}`;\n\n return new Request(fullUrl, {\n method: config.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: getAcceptHeader(options?.includeODataAnnotations),\n },\n body: config.body,\n });\n }\n\n async processResponse(\n response: Response,\n _options?: ExecuteOptions,\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = getTableName(this.table);\n const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);\n return { data: undefined, error };\n }\n\n // Check for empty response (204 No Content)\n const text = await response.text();\n if (!text || text.trim() === \"\") {\n // For 204 No Content, check the fmodata.affected_rows header\n const affectedRows = response.headers.get(\"fmodata.affected_rows\");\n const updatedCount = affectedRows ? Number.parseInt(affectedRows, 10) : 1;\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n\n const rawResponse = JSON.parse(text);\n\n // Validate and transform input data using input validators (writeValidators)\n // This is needed for processResponse because it's called from batch operations\n // where the data hasn't been validated yet\n let _validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n try {\n _validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type\n } as any;\n }\n }\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: rawResponse as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof rawResponse === \"number\") {\n updatedCount = rawResponse;\n } else if (rawResponse && typeof rawResponse === \"object\") {\n // Check if the response has a count property (fallback)\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n updatedCount = (rawResponse as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n}\n"],"names":["getTableIdHelper","updatedCount"],"mappings":"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"}
1
+ {"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { FMTable, InferSchemaOutputFromFMTable } from \"../orm/table\";\nimport { getBaseTableConfig, getTableId as getTableIdHelper, getTableName, isUsingEntityIds } from \"../orm/table\";\nimport { transformFieldNamesToIds } from \"../transform\";\nimport type { ExecutableBuilder, ExecuteMethodOptions, ExecuteOptions, ExecutionContext, Result } from \"../types\";\nimport { getAcceptHeader } from \"../types\";\nimport { validateAndTransformInput } from \"../validation\";\nimport { parseErrorResponse } from \"./error-parser\";\nimport { QueryBuilder } from \"./query-builder\";\n\n/**\n * Initial update builder returned from EntitySet.update(data)\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class UpdateBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n Occ extends FMTable<any, any>,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> {\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly table: Occ;\n private readonly data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private readonly returnPreference: ReturnPreference;\n\n private readonly databaseUseEntityIds: boolean;\n private readonly databaseIncludeSpecialColumns: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n databaseIncludeSpecialColumns?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n this.databaseIncludeSpecialColumns = config.databaseIncludeSpecialColumns ?? false;\n }\n\n /**\n * Update a single record by ID\n * Returns updated count by default, or full record if returnFullRecord was set to true\n */\n byId(id: string | number): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns updated count by default, or full record if returnFullRecord was set to true\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(fn: (q: QueryBuilder<Occ>) => QueryBuilder<Occ>): ExecutableUpdateBuilder<Occ, true, ReturnPreference> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<Occ>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Let the user configure it\n const configuredBuilder = fn(queryBuilder);\n\n return new ExecutableUpdateBuilder<Occ, true, ReturnPreference>({\n occurrence: this.table,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n returnPreference: this.returnPreference,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Can return either updated count or full record based on returnFullRecord option\n */\nexport class ExecutableUpdateBuilder<\n // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration\n Occ extends FMTable<any, any>,\n _IsByFilter extends boolean,\n ReturnPreference extends \"minimal\" | \"representation\" = \"minimal\",\n> implements\n ExecutableBuilder<ReturnPreference extends \"minimal\" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>\n{\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n private readonly table: Occ;\n private readonly data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n private readonly mode: \"byId\" | \"byFilter\";\n private readonly recordId?: string | number;\n private readonly queryBuilder?: QueryBuilder<Occ>;\n private readonly returnPreference: ReturnPreference;\n private readonly databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence: Occ;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<InferSchemaOutputFromFMTable<Occ>>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<Occ>;\n returnPreference: ReturnPreference;\n databaseUseEntityIds?: boolean;\n }) {\n this.table = config.occurrence;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n this.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n this.returnPreference = config.returnPreference;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Helper to merge database-level useEntityIds with per-request options\n */\n private mergeExecuteOptions(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n // If useEntityIds is not set in options, use the database-level setting\n return {\n ...options,\n useEntityIds: options?.useEntityIds ?? this.databaseUseEntityIds,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n const contextDefault = this.context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIds ?? contextDefault;\n\n if (shouldUseIds) {\n if (!isUsingEntityIds(this.table)) {\n throw new Error(\n `useEntityIds is true but table \"${getTableName(this.table)}\" does not have entity IDs configured`,\n );\n }\n return getTableIdHelper(this.table);\n }\n\n return getTableName(this.table);\n }\n\n async execute(\n options?: ExecuteMethodOptions<ExecuteOptions>,\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>\n > {\n // Merge database-level useEntityIds with per-request options\n const mergedOptions = this.mergeExecuteOptions(options);\n\n // Get table identifier with override support\n const tableId = this.getTableId(mergedOptions.useEntityIds);\n\n // Validate and transform input data using input validators (writeValidators)\n let validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n\n try {\n validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n // If validation fails, return error immediately\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type\n } as any;\n }\n }\n\n // Transform field names to FMFIDs if using entity IDs\n // Only transform if useEntityIds resolves to true (respects per-request override)\n const shouldUseIds = mergedOptions.useEntityIds ?? false;\n\n const transformedData =\n this.table && shouldUseIds ? transformFieldNamesToIds(validatedData, this.table) : validatedData;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Update by filter: PATCH /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\n // The query string will have the tableId already transformed by QueryBuilder\n // Remove the leading \"/\" and table name from the query string as we'll build our own URL\n const tableName = getTableName(this.table);\n let queryParams: string;\n if (queryString.startsWith(`/${tableId}`)) {\n queryParams = queryString.slice(`/${tableId}`.length);\n } else if (queryString.startsWith(`/${tableName}`)) {\n queryParams = queryString.slice(`/${tableName}`.length);\n } else {\n queryParams = queryString;\n }\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Set Prefer header based on returnPreference\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (this.returnPreference === \"representation\") {\n headers.Prefer = \"return=representation\";\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers,\n body: JSON.stringify(transformedData),\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: response as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof response === \"number\") {\n updatedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n updatedCount = (response as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Request body can be any JSON-serializable value\n getRequestConfig(): { method: string; url: string; body?: any } {\n // For batch operations, use database-level setting (no per-request override available here)\n // Note: Input validation happens in execute() and processResponse() for batch operations\n const tableId = this.getTableId(this.databaseUseEntityIds);\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData =\n this.table && this.databaseUseEntityIds ? transformFieldNamesToIds(this.data, this.table) : this.data;\n\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based update\");\n }\n\n const queryString = this.queryBuilder.getQueryString();\n const tableName = getTableName(this.table);\n let queryParams: string;\n if (queryString.startsWith(`/${tableId}`)) {\n queryParams = queryString.slice(`/${tableId}`.length);\n } else if (queryString.startsWith(`/${tableName}`)) {\n queryParams = queryString.slice(`/${tableName}`.length);\n } else {\n queryParams = queryString;\n }\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\n body: JSON.stringify(transformedData),\n };\n }\n\n toRequest(baseUrl: string, options?: ExecuteOptions): Request {\n const config = this.getRequestConfig();\n const fullUrl = `${baseUrl}${config.url}`;\n\n return new Request(fullUrl, {\n method: config.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: getAcceptHeader(options?.includeODataAnnotations),\n },\n body: config.body,\n });\n }\n\n async processResponse(\n response: Response,\n _options?: ExecuteOptions,\n ): Promise<\n Result<ReturnPreference extends \"minimal\" ? { updatedCount: number } : InferSchemaOutputFromFMTable<Occ>>\n > {\n // Check for error responses (important for batch operations)\n if (!response.ok) {\n const tableName = getTableName(this.table);\n const error = await parseErrorResponse(response, response.url || `/${this.databaseName}/${tableName}`);\n return { data: undefined, error };\n }\n\n // Check for empty response (204 No Content)\n const text = await response.text();\n if (!text || text.trim() === \"\") {\n // For 204 No Content, check the fmodata.affected_rows header\n const affectedRows = response.headers.get(\"fmodata.affected_rows\");\n const updatedCount = affectedRows ? Number.parseInt(affectedRows, 10) : 1;\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n\n const rawResponse = JSON.parse(text);\n\n // Validate and transform input data using input validators (writeValidators)\n // This is needed for processResponse because it's called from batch operations\n // where the data hasn't been validated yet\n let _validatedData = this.data;\n if (this.table) {\n const baseTableConfig = getBaseTableConfig(this.table);\n const inputSchema = baseTableConfig.inputSchema;\n try {\n _validatedData = await validateAndTransformInput(this.data, inputSchema);\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type\n } as any;\n }\n }\n\n // Handle based on return preference\n if (this.returnPreference === \"representation\") {\n // Return the full updated record\n return {\n data: rawResponse as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n // Return updated count (minimal)\n let updatedCount = 0;\n\n if (typeof rawResponse === \"number\") {\n updatedCount = rawResponse;\n } else if (rawResponse && typeof rawResponse === \"object\") {\n // Check if the response has a count property (fallback)\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n updatedCount = (rawResponse as any).updatedCount || 0;\n }\n\n return {\n data: { updatedCount } as ReturnPreference extends \"minimal\"\n ? { updatedCount: number }\n : InferSchemaOutputFromFMTable<Occ>,\n error: undefined,\n };\n }\n}\n"],"names":["getTableIdHelper","updatedCount"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-builder.js","sources":["../../../src/client/webhook-builder.ts"],"sourcesContent":["import { type FMTable, getTableName } from \"../orm\";\nimport { type Column, isColumn } from \"../orm/column\";\nimport { FilterExpression } from \"../orm/operators\";\nimport type { ExecuteMethodOptions, ExecutionContext } from \"../types\";\nimport { formatSelectFields } from \"./builders/select-utils\";\n\nexport interface Webhook<TableName = string> {\n webhook: string;\n headers?: Record<string, string>;\n tableName: TableName;\n notifySchemaChanges?: boolean;\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n select?: string | Column<any, any, any>[];\n filter?: string | FilterExpression;\n}\n\n/**\n * Webhook information returned by the API\n */\nexport interface WebhookInfo {\n webHookID: number;\n tableName: string;\n url: string;\n headers?: Record<string, string>;\n notifySchemaChanges: boolean;\n select: string;\n filter: string;\n pendingOperations: unknown[];\n}\n\n/**\n * Response from listing all webhooks\n */\nexport interface WebhookListResponse {\n Status: string;\n WebHook: WebhookInfo[];\n}\n\n/**\n * Response from adding a webhook\n */\nexport interface WebhookAddResponse {\n webHookResult: {\n webHookID: number;\n };\n}\n\nexport class WebhookManager {\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n\n constructor(databaseName: string, context: ExecutionContext) {\n this.databaseName = databaseName;\n this.context = context;\n }\n\n /**\n * Adds a new webhook to the database.\n * @param webhook - The webhook configuration object\n * @param webhook.webhook - The webhook URL to call\n * @param webhook.tableName - The FMTable instance for the table to monitor\n * @param webhook.headers - Optional custom headers to include in webhook requests\n * @param webhook.notifySchemaChanges - Whether to notify on schema changes\n * @param webhook.select - Optional field selection (string or array of Column references)\n * @param webhook.filter - Optional filter (string or FilterExpression)\n * @returns Promise resolving to the created webhook data with ID\n * @example\n * ```ts\n * const result = await db.webhook.add({\n * webhook: \"https://example.com/webhook\",\n * tableName: contactsTable,\n * headers: { \"X-Custom-Header\": \"value\" },\n * });\n * // result.webHookResult.webHookID contains the new webhook ID\n * ```\n * @example\n * ```ts\n * // Using filter expressions and column arrays (same DX as query builder)\n * const result = await db.webhook.add({\n * webhook: \"https://example.com/webhook\",\n * tableName: contacts,\n * filter: eq(contacts.name, \"John\"),\n * select: [contacts.name, contacts.PrimaryKey],\n * });\n * ```\n */\n async add(webhook: Webhook<FMTable>, options?: ExecuteMethodOptions): Promise<WebhookAddResponse> {\n // Extract the string table name from the FMTable instance\n const tableName = getTableName(webhook.tableName);\n\n // Get useEntityIds setting (check options first, then context, default to false)\n const useEntityIds = options?.useEntityIds ?? this.context._getUseEntityIds?.() ?? false;\n\n // Transform filter if it's a FilterExpression\n let filter: string | undefined;\n if (webhook.filter !== undefined) {\n if (webhook.filter instanceof FilterExpression) {\n filter = webhook.filter.toODataFilter(useEntityIds);\n } else {\n filter = webhook.filter;\n }\n }\n\n // Transform select if it's an array of Columns\n let select: string | undefined;\n if (webhook.select !== undefined) {\n if (Array.isArray(webhook.select)) {\n // Extract field identifiers from columns or use strings as-is\n const fieldNames = webhook.select.map((item) => {\n if (isColumn(item)) {\n return item.getFieldIdentifier(useEntityIds);\n }\n return String(item);\n });\n // Use formatSelectFields to properly format the select string\n select = formatSelectFields(fieldNames, webhook.tableName, useEntityIds);\n } else {\n // Already a string, use as-is\n select = webhook.select;\n }\n }\n\n // Create request body with string table name and transformed filter/select\n const requestBody: {\n webhook: string;\n headers?: Record<string, string>;\n tableName: string;\n notifySchemaChanges?: boolean;\n select?: string;\n filter?: string;\n } = {\n webhook: webhook.webhook,\n tableName,\n };\n\n if (webhook.headers !== undefined) {\n requestBody.headers = webhook.headers;\n }\n if (webhook.notifySchemaChanges !== undefined) {\n requestBody.notifySchemaChanges = webhook.notifySchemaChanges;\n }\n if (select !== undefined) {\n requestBody.select = select;\n }\n if (filter !== undefined) {\n requestBody.filter = filter;\n }\n\n const result = await this.context._makeRequest<WebhookAddResponse>(`/${this.databaseName}/Webhook.Add`, {\n method: \"POST\",\n body: JSON.stringify(requestBody),\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n /**\n * Deletes a webhook by ID.\n * @param webhookId - The ID of the webhook to delete\n * @returns Promise that resolves when the webhook is deleted\n * @example\n * ```ts\n * await db.webhook.remove(1);\n * ```\n */\n async remove(webhookId: number, options?: ExecuteMethodOptions): Promise<void> {\n const result = await this.context._makeRequest(`/${this.databaseName}/Webhook.Delete(${webhookId})`, {\n method: \"POST\",\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n }\n\n /**\n * Gets a webhook by ID.\n * @param webhookId - The ID of the webhook to retrieve\n * @returns Promise resolving to the webhook data\n * @example\n * ```ts\n * const webhook = await db.webhook.get(1);\n * // webhook.webHookID, webhook.tableName, webhook.url, etc.\n * ```\n */\n async get(webhookId: number, options?: ExecuteMethodOptions): Promise<WebhookInfo> {\n const result = await this.context._makeRequest<WebhookInfo>(\n `/${this.databaseName}/Webhook.Get(${webhookId})`,\n options,\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n /**\n * Lists all webhooks.\n * @returns Promise resolving to webhook list response with status and webhooks array\n * @example\n * ```ts\n * const result = await db.webhook.list();\n * // result.Status contains the status\n * // result.WebHook contains the array of webhooks\n * ```\n */\n async list(options?: ExecuteMethodOptions): Promise<WebhookListResponse> {\n const result = await this.context._makeRequest<WebhookListResponse>(\n `/${this.databaseName}/Webhook.GetAll`,\n options,\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n /**\n * Invokes a webhook by ID, optionally for specific row IDs.\n * @param webhookId - The ID of the webhook to invoke\n * @param options - Optional configuration\n * @param options.rowIDs - Array of row IDs to trigger the webhook for\n * @returns Promise resolving to the invocation result (type unknown until API behavior is confirmed)\n * @example\n * ```ts\n * // Invoke for all rows\n * await db.webhook.invoke(1);\n *\n * // Invoke for specific rows\n * await db.webhook.invoke(1, { rowIDs: [63, 61] });\n * ```\n */\n async invoke(\n webhookId: number,\n options?: { rowIDs?: number[] },\n executeOptions?: ExecuteMethodOptions,\n ): Promise<unknown> {\n const body: { rowIDs?: number[] } = {};\n if (options?.rowIDs !== undefined) {\n body.rowIDs = options.rowIDs;\n }\n\n const result = await this.context._makeRequest<unknown>(`/${this.databaseName}/Webhook.Invoke(${webhookId})`, {\n method: \"POST\",\n body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,\n ...executeOptions,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook-builder.js","sources":["../../../src/client/webhook-builder.ts"],"sourcesContent":["import { type FMTable, getTableName } from \"../orm\";\nimport { type Column, isColumn } from \"../orm/column\";\nimport { FilterExpression } from \"../orm/operators\";\nimport type { ExecuteMethodOptions, ExecutionContext } from \"../types\";\nimport { formatSelectFields } from \"./builders/select-utils\";\n\nexport interface Webhook<TableName = string> {\n webhook: string;\n headers?: Record<string, string>;\n tableName: TableName;\n notifySchemaChanges?: boolean;\n // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration\n select?: string | Column<any, any, any>[];\n filter?: string | FilterExpression;\n}\n\n/**\n * Webhook information returned by the API\n */\nexport interface WebhookInfo {\n webHookID: number;\n tableName: string;\n url: string;\n headers?: Record<string, string>;\n notifySchemaChanges: boolean;\n select: string;\n filter: string;\n pendingOperations: unknown[];\n}\n\n/**\n * Response from listing all webhooks\n */\nexport interface WebhookListResponse {\n Status: string;\n WebHook: WebhookInfo[];\n}\n\n/**\n * Response from adding a webhook\n */\nexport interface WebhookAddResponse {\n webHookResult: {\n webHookID: number;\n };\n}\n\nexport class WebhookManager {\n private readonly databaseName: string;\n private readonly context: ExecutionContext;\n\n constructor(databaseName: string, context: ExecutionContext) {\n this.databaseName = databaseName;\n this.context = context;\n }\n\n /**\n * Adds a new webhook to the database.\n * @param webhook - The webhook configuration object\n * @param webhook.webhook - The webhook URL to call\n * @param webhook.tableName - The FMTable instance for the table to monitor\n * @param webhook.headers - Optional custom headers to include in webhook requests\n * @param webhook.notifySchemaChanges - Whether to notify on schema changes\n * @param webhook.select - Optional field selection (string or array of Column references)\n * @param webhook.filter - Optional filter (string or FilterExpression)\n * @returns Promise resolving to the created webhook data with ID\n * @example\n * ```ts\n * const result = await db.webhook.add({\n * webhook: \"https://example.com/webhook\",\n * tableName: contactsTable,\n * headers: { \"X-Custom-Header\": \"value\" },\n * });\n * // result.webHookResult.webHookID contains the new webhook ID\n * ```\n * @example\n * ```ts\n * // Using filter expressions and column arrays (same DX as query builder)\n * const result = await db.webhook.add({\n * webhook: \"https://example.com/webhook\",\n * tableName: contacts,\n * filter: eq(contacts.name, \"John\"),\n * select: [contacts.name, contacts.PrimaryKey],\n * });\n * ```\n */\n async add(webhook: Webhook<FMTable>, options?: ExecuteMethodOptions): Promise<WebhookAddResponse> {\n // Extract the string table name from the FMTable instance\n const tableName = getTableName(webhook.tableName);\n\n // Get useEntityIds setting (check options first, then context, default to false)\n const useEntityIds = options?.useEntityIds ?? this.context._getUseEntityIds?.() ?? false;\n\n // Transform filter if it's a FilterExpression\n let filter: string | undefined;\n if (webhook.filter !== undefined) {\n if (webhook.filter instanceof FilterExpression) {\n filter = webhook.filter.toODataFilter(useEntityIds);\n } else {\n filter = webhook.filter;\n }\n }\n\n // Transform select if it's an array of Columns\n let select: string | undefined;\n if (webhook.select !== undefined) {\n if (Array.isArray(webhook.select)) {\n // Extract field identifiers from columns or use strings as-is\n const fieldNames = webhook.select.map((item) => {\n if (isColumn(item)) {\n return item.getFieldIdentifier(useEntityIds);\n }\n return String(item);\n });\n // Use formatSelectFields to properly format the select string\n select = formatSelectFields(fieldNames, webhook.tableName, useEntityIds);\n } else {\n // Already a string, use as-is\n select = webhook.select;\n }\n }\n\n // Create request body with string table name and transformed filter/select\n const requestBody: {\n webhook: string;\n headers?: Record<string, string>;\n tableName: string;\n notifySchemaChanges?: boolean;\n select?: string;\n filter?: string;\n } = {\n webhook: webhook.webhook,\n tableName,\n };\n\n if (webhook.headers !== undefined) {\n requestBody.headers = webhook.headers;\n }\n if (webhook.notifySchemaChanges !== undefined) {\n requestBody.notifySchemaChanges = webhook.notifySchemaChanges;\n }\n if (select !== undefined) {\n requestBody.select = select;\n }\n if (filter !== undefined) {\n requestBody.filter = filter;\n }\n\n const result = await this.context._makeRequest<WebhookAddResponse>(`/${this.databaseName}/Webhook.Add`, {\n method: \"POST\",\n body: JSON.stringify(requestBody),\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n /**\n * Deletes a webhook by ID.\n * @param webhookId - The ID of the webhook to delete\n * @returns Promise that resolves when the webhook is deleted\n * @example\n * ```ts\n * await db.webhook.remove(1);\n * ```\n */\n async remove(webhookId: number, options?: ExecuteMethodOptions): Promise<void> {\n const result = await this.context._makeRequest(`/${this.databaseName}/Webhook.Delete(${webhookId})`, {\n method: \"POST\",\n ...options,\n });\n\n if (result.error) {\n throw result.error;\n }\n }\n\n /**\n * Gets a webhook by ID.\n * @param webhookId - The ID of the webhook to retrieve\n * @returns Promise resolving to the webhook data\n * @example\n * ```ts\n * const webhook = await db.webhook.get(1);\n * // webhook.webHookID, webhook.tableName, webhook.url, etc.\n * ```\n */\n async get(webhookId: number, options?: ExecuteMethodOptions): Promise<WebhookInfo> {\n const result = await this.context._makeRequest<WebhookInfo>(\n `/${this.databaseName}/Webhook.Get(${webhookId})`,\n options,\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n /**\n * Lists all webhooks.\n * @returns Promise resolving to webhook list response with status and webhooks array\n * @example\n * ```ts\n * const result = await db.webhook.list();\n * // result.Status contains the status\n * // result.WebHook contains the array of webhooks\n * ```\n */\n async list(options?: ExecuteMethodOptions): Promise<WebhookListResponse> {\n const result = await this.context._makeRequest<WebhookListResponse>(\n `/${this.databaseName}/Webhook.GetAll`,\n options,\n );\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n\n /**\n * Invokes a webhook by ID, optionally for specific row IDs.\n * @param webhookId - The ID of the webhook to invoke\n * @param options - Optional configuration\n * @param options.rowIDs - Array of row IDs to trigger the webhook for\n * @returns Promise resolving to the invocation result (type unknown until API behavior is confirmed)\n * @example\n * ```ts\n * // Invoke for all rows\n * await db.webhook.invoke(1);\n *\n * // Invoke for specific rows\n * await db.webhook.invoke(1, { rowIDs: [63, 61] });\n * ```\n */\n async invoke(\n webhookId: number,\n options?: { rowIDs?: number[] },\n executeOptions?: ExecuteMethodOptions,\n ): Promise<unknown> {\n const body: { rowIDs?: number[] } = {};\n if (options?.rowIDs !== undefined) {\n body.rowIDs = options.rowIDs;\n }\n\n const result = await this.context._makeRequest<unknown>(`/${this.databaseName}/Webhook.Invoke(${webhookId})`, {\n method: \"POST\",\n body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,\n ...executeOptions,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n return result.data;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Base class for all fmodata errors\n */\nexport abstract class FMODataError extends Error {\n abstract readonly kind: string;\n readonly timestamp: Date;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = this.constructor.name;\n this.timestamp = new Date();\n }\n}\n\n// ============================================\n// HTTP Errors (with status codes)\n// ============================================\n\nexport class HTTPError extends FMODataError {\n readonly kind = \"HTTPError\" as const;\n readonly url: string;\n readonly status: number;\n readonly statusText: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n readonly response?: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n constructor(url: string, status: number, statusText: string, response?: any) {\n super(`HTTP ${status} ${statusText} for ${url}`);\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n this.response = response;\n }\n\n // Helper methods for common status checks\n is4xx(): boolean {\n return this.status >= 400 && this.status < 500;\n }\n\n is5xx(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isForbidden(): boolean {\n return this.status === 403;\n }\n}\n\n// ============================================\n// OData Specific Errors\n// ============================================\n\nexport class ODataError extends FMODataError {\n readonly kind = \"ODataError\" as const;\n readonly url: string;\n readonly code?: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n readonly details?: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n constructor(url: string, message: string, code?: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = code;\n this.details = details;\n }\n}\n\nexport class SchemaLockedError extends FMODataError {\n readonly kind = \"SchemaLockedError\" as const;\n readonly url: string;\n readonly code: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n readonly details?: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n constructor(url: string, message: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = \"303\";\n this.details = details;\n }\n}\n\n// ============================================\n// Validation Errors\n// ============================================\n\nexport class ValidationError extends FMODataError {\n readonly kind = \"ValidationError\" as const;\n readonly field?: string;\n readonly issues: readonly StandardSchemaV1.Issue[];\n readonly value?: unknown;\n\n constructor(\n message: string,\n issues: readonly StandardSchemaV1.Issue[],\n options?: {\n field?: string;\n value?: unknown;\n cause?: Error[\"cause\"];\n },\n ) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.field = options?.field;\n this.issues = issues;\n this.value = options?.value;\n }\n}\n\nexport class ResponseStructureError extends FMODataError {\n readonly kind = \"ResponseStructureError\" as const;\n readonly expected: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n readonly received: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n constructor(expected: string, received: any) {\n super(`Invalid response structure: expected ${expected}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class RecordCountMismatchError extends FMODataError {\n readonly kind = \"RecordCountMismatchError\" as const;\n readonly expected: number | \"one\" | \"at-most-one\";\n readonly received: number;\n\n constructor(expected: number | \"one\" | \"at-most-one\", received: number) {\n const expectedStr = typeof expected === \"number\" ? expected : expected;\n super(`Expected ${expectedStr} record(s), but received ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidLocationHeaderError extends FMODataError {\n readonly kind = \"InvalidLocationHeaderError\" as const;\n readonly locationHeader?: string;\n\n constructor(message: string, locationHeader?: string) {\n super(message);\n this.locationHeader = locationHeader;\n }\n}\n\nexport class ResponseParseError extends FMODataError {\n readonly kind = \"ResponseParseError\" as const;\n readonly url: string;\n readonly rawText?: string;\n\n constructor(url: string, message: string, options?: { rawText?: string; cause?: Error }) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.url = url;\n this.rawText = options?.rawText;\n }\n}\n\nexport class BatchTruncatedError extends FMODataError {\n readonly kind = \"BatchTruncatedError\" as const;\n readonly operationIndex: number;\n readonly failedAtIndex: number;\n\n constructor(operationIndex: number, failedAtIndex: number) {\n super(`Operation ${operationIndex} was not executed because operation ${failedAtIndex} failed`);\n this.operationIndex = operationIndex;\n this.failedAtIndex = failedAtIndex;\n }\n}\n\n// ============================================\n// Type Guards\n// ============================================\n\nexport function isHTTPError(error: unknown): error is HTTPError {\n return error instanceof HTTPError;\n}\n\nexport function isValidationError(error: unknown): error is ValidationError {\n return error instanceof ValidationError;\n}\n\nexport function isODataError(error: unknown): error is ODataError {\n return error instanceof ODataError;\n}\n\nexport function isSchemaLockedError(error: unknown): error is SchemaLockedError {\n return error instanceof SchemaLockedError;\n}\n\nexport function isResponseStructureError(error: unknown): error is ResponseStructureError {\n return error instanceof ResponseStructureError;\n}\n\nexport function isRecordCountMismatchError(error: unknown): error is RecordCountMismatchError {\n return error instanceof RecordCountMismatchError;\n}\n\nexport function isResponseParseError(error: unknown): error is ResponseParseError {\n return error instanceof ResponseParseError;\n}\n\nexport function isBatchTruncatedError(error: unknown): error is BatchTruncatedError {\n return error instanceof BatchTruncatedError;\n}\n\nexport function isFMODataError(error: unknown): error is FMODataError {\n return error instanceof FMODataError;\n}\n\n// ============================================\n// Union type for all possible errors\n// ============================================\n\n// Re-export ffetch errors (they'll be imported from @fetchkit/ffetch)\nexport type {\n AbortError,\n CircuitOpenError,\n NetworkError,\n RetryLimitError,\n TimeoutError,\n} from \"@fetchkit/ffetch\";\n\nexport type FMODataErrorType =\n | import(\"@fetchkit/ffetch\").TimeoutError\n | import(\"@fetchkit/ffetch\").AbortError\n | import(\"@fetchkit/ffetch\").NetworkError\n | import(\"@fetchkit/ffetch\").RetryLimitError\n | import(\"@fetchkit/ffetch\").CircuitOpenError\n | HTTPError\n | ODataError\n | SchemaLockedError\n | ValidationError\n | ResponseStructureError\n | RecordCountMismatchError\n | InvalidLocationHeaderError\n | ResponseParseError\n | BatchTruncatedError;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sources":["../../src/errors.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Base class for all fmodata errors\n */\nexport abstract class FMODataError extends Error {\n abstract readonly kind: string;\n readonly timestamp: Date;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = this.constructor.name;\n this.timestamp = new Date();\n }\n}\n\n// ============================================\n// HTTP Errors (with status codes)\n// ============================================\n\nexport class HTTPError extends FMODataError {\n readonly kind = \"HTTPError\" as const;\n readonly url: string;\n readonly status: number;\n readonly statusText: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n readonly response?: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n constructor(url: string, status: number, statusText: string, response?: any) {\n super(`HTTP ${status} ${statusText} for ${url}`);\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n this.response = response;\n }\n\n // Helper methods for common status checks\n is4xx(): boolean {\n return this.status >= 400 && this.status < 500;\n }\n\n is5xx(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isForbidden(): boolean {\n return this.status === 403;\n }\n}\n\n// ============================================\n// OData Specific Errors\n// ============================================\n\nexport class ODataError extends FMODataError {\n readonly kind = \"ODataError\" as const;\n readonly url: string;\n readonly code?: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n readonly details?: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n constructor(url: string, message: string, code?: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = code;\n this.details = details;\n }\n}\n\nexport class SchemaLockedError extends FMODataError {\n readonly kind = \"SchemaLockedError\" as const;\n readonly url: string;\n readonly code: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n readonly details?: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API\n constructor(url: string, message: string, details?: any) {\n super(`OData error: ${message}`);\n this.url = url;\n this.code = \"303\";\n this.details = details;\n }\n}\n\n// ============================================\n// Validation Errors\n// ============================================\n\nexport class ValidationError extends FMODataError {\n readonly kind = \"ValidationError\" as const;\n readonly field?: string;\n readonly issues: readonly StandardSchemaV1.Issue[];\n readonly value?: unknown;\n\n constructor(\n message: string,\n issues: readonly StandardSchemaV1.Issue[],\n options?: {\n field?: string;\n value?: unknown;\n cause?: Error[\"cause\"];\n },\n ) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.field = options?.field;\n this.issues = issues;\n this.value = options?.value;\n }\n}\n\nexport class ResponseStructureError extends FMODataError {\n readonly kind = \"ResponseStructureError\" as const;\n readonly expected: string;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n readonly received: any;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API\n constructor(expected: string, received: any) {\n super(`Invalid response structure: expected ${expected}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class RecordCountMismatchError extends FMODataError {\n readonly kind = \"RecordCountMismatchError\" as const;\n readonly expected: number | \"one\" | \"at-most-one\";\n readonly received: number;\n\n constructor(expected: number | \"one\" | \"at-most-one\", received: number) {\n const expectedStr = typeof expected === \"number\" ? expected : expected;\n super(`Expected ${expectedStr} record(s), but received ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidLocationHeaderError extends FMODataError {\n readonly kind = \"InvalidLocationHeaderError\" as const;\n readonly locationHeader?: string;\n\n constructor(message: string, locationHeader?: string) {\n super(message);\n this.locationHeader = locationHeader;\n }\n}\n\nexport class ResponseParseError extends FMODataError {\n readonly kind = \"ResponseParseError\" as const;\n readonly url: string;\n readonly rawText?: string;\n\n constructor(url: string, message: string, options?: { rawText?: string; cause?: Error }) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.url = url;\n this.rawText = options?.rawText;\n }\n}\n\nexport class BatchTruncatedError extends FMODataError {\n readonly kind = \"BatchTruncatedError\" as const;\n readonly operationIndex: number;\n readonly failedAtIndex: number;\n\n constructor(operationIndex: number, failedAtIndex: number) {\n super(`Operation ${operationIndex} was not executed because operation ${failedAtIndex} failed`);\n this.operationIndex = operationIndex;\n this.failedAtIndex = failedAtIndex;\n }\n}\n\n// ============================================\n// Type Guards\n// ============================================\n\nexport function isHTTPError(error: unknown): error is HTTPError {\n return error instanceof HTTPError;\n}\n\nexport function isValidationError(error: unknown): error is ValidationError {\n return error instanceof ValidationError;\n}\n\nexport function isODataError(error: unknown): error is ODataError {\n return error instanceof ODataError;\n}\n\nexport function isSchemaLockedError(error: unknown): error is SchemaLockedError {\n return error instanceof SchemaLockedError;\n}\n\nexport function isResponseStructureError(error: unknown): error is ResponseStructureError {\n return error instanceof ResponseStructureError;\n}\n\nexport function isRecordCountMismatchError(error: unknown): error is RecordCountMismatchError {\n return error instanceof RecordCountMismatchError;\n}\n\nexport function isResponseParseError(error: unknown): error is ResponseParseError {\n return error instanceof ResponseParseError;\n}\n\nexport function isBatchTruncatedError(error: unknown): error is BatchTruncatedError {\n return error instanceof BatchTruncatedError;\n}\n\nexport function isFMODataError(error: unknown): error is FMODataError {\n return error instanceof FMODataError;\n}\n\n// ============================================\n// Union type for all possible errors\n// ============================================\n\n// Re-export ffetch errors (they'll be imported from @fetchkit/ffetch)\nexport type {\n AbortError,\n CircuitOpenError,\n NetworkError,\n RetryLimitError,\n TimeoutError,\n} from \"@fetchkit/ffetch\";\n\nexport type FMODataErrorType =\n | import(\"@fetchkit/ffetch\").TimeoutError\n | import(\"@fetchkit/ffetch\").AbortError\n | import(\"@fetchkit/ffetch\").NetworkError\n | import(\"@fetchkit/ffetch\").RetryLimitError\n | import(\"@fetchkit/ffetch\").CircuitOpenError\n | HTTPError\n | ODataError\n | SchemaLockedError\n | ValidationError\n | ResponseStructureError\n | RecordCountMismatchError\n | InvalidLocationHeaderError\n | ResponseParseError\n | BatchTruncatedError;\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sources":["../../src/logger.ts"],"sourcesContent":["export const TTY_COLORS = {\n reset: \"\\x1b[0m\",\n bright: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n undim: \"\\x1b[22m\",\n underscore: \"\\x1b[4m\",\n blink: \"\\x1b[5m\",\n reverse: \"\\x1b[7m\",\n hidden: \"\\x1b[8m\",\n fg: {\n black: \"\\x1b[30m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n },\n bg: {\n black: \"\\x1b[40m\",\n red: \"\\x1b[41m\",\n green: \"\\x1b[42m\",\n yellow: \"\\x1b[43m\",\n blue: \"\\x1b[44m\",\n magenta: \"\\x1b[45m\",\n cyan: \"\\x1b[46m\",\n white: \"\\x1b[47m\",\n },\n} as const;\n\nexport type LogLevel = \"debug\" | \"info\" | \"success\" | \"warn\" | \"error\";\n\nexport const levels = [\"debug\", \"info\", \"success\", \"warn\", \"error\"] as const;\n\nexport function shouldPublishLog(currentLogLevel: LogLevel, logLevel: LogLevel): boolean {\n return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);\n}\n\nexport interface Logger {\n disabled?: boolean | undefined;\n disableColors?: boolean | undefined;\n level?: Exclude<LogLevel, \"success\"> | undefined;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic log arguments from user code\n log?: ((level: Exclude<LogLevel, \"success\">, message: string, ...args: any[]) => void) | undefined;\n}\n\nexport type LogHandlerParams = Parameters<NonNullable<Logger[\"log\"]>> extends [LogLevel, ...infer Rest] ? Rest : never;\n\nconst levelColors: Record<LogLevel, string> = {\n info: TTY_COLORS.fg.blue,\n success: TTY_COLORS.fg.green,\n warn: TTY_COLORS.fg.yellow,\n error: TTY_COLORS.fg.red,\n debug: TTY_COLORS.fg.magenta,\n};\n\nconst formatMessage = (level: LogLevel, message: string, colorsEnabled: boolean): string => {\n const timestamp = new Date().toISOString();\n\n if (colorsEnabled) {\n return `${TTY_COLORS.dim}${timestamp}${TTY_COLORS.reset} ${\n levelColors[level]\n }${level.toUpperCase()}${TTY_COLORS.reset} ${TTY_COLORS.bright}[FMODATA]:${TTY_COLORS.reset} ${message}`;\n }\n\n return `${timestamp} ${level.toUpperCase()} [FMODATA]: ${message}`;\n};\n\nexport type InternalLogger = {\n [K in LogLevel]: (...params: LogHandlerParams) => void;\n} & {\n get level(): LogLevel;\n};\n\nexport const createLogger = (options?: Logger | undefined): InternalLogger => {\n const enabled = options?.disabled !== true;\n const logLevel = options?.level ?? \"error\";\n\n const colorsEnabled = options?.disableColors !== true;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic log arguments from user code\n const LogFunc = (level: LogLevel, message: string, args: any[] = []): void => {\n if (!(enabled && shouldPublishLog(logLevel, level))) {\n return;\n }\n\n const formattedMessage = formatMessage(level, message, colorsEnabled);\n\n if (!options || typeof options.log !== \"function\") {\n if (level === \"error\") {\n console.error(formattedMessage, ...args);\n } else if (level === \"warn\") {\n console.warn(formattedMessage, ...args);\n } else {\n console.log(formattedMessage, ...args);\n }\n return;\n }\n\n options.log(level === \"success\" ? \"info\" : level, message, ...args);\n };\n\n const logger = Object.fromEntries(\n levels.map((level) => [level, (...[message, ...args]: LogHandlerParams) => LogFunc(level, message, args)]),\n ) as Record<LogLevel, (...params: LogHandlerParams) => void>;\n\n return {\n ...logger,\n get level() {\n return logLevel;\n },\n };\n};\n\nexport const logger = createLogger();\n"],"names":["logger"],"mappings":"AAAO,MAAM,aAAa;AAAA,EACxB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EAML,IAAI;AAAA,IAEF,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EAGX;AAWF;AAIO,MAAM,SAAS,CAAC,SAAS,QAAQ,WAAW,QAAQ,OAAO;AAElD,SAAA,iBAAiB,iBAA2B,UAA6B;AACvF,SAAO,OAAO,QAAQ,QAAQ,KAAK,OAAO,QAAQ,eAAe;AACnE;AAYA,MAAM,cAAwC;AAAA,EAC5C,MAAM,WAAW,GAAG;AAAA,EACpB,SAAS,WAAW,GAAG;AAAA,EACvB,MAAM,WAAW,GAAG;AAAA,EACpB,OAAO,WAAW,GAAG;AAAA,EACrB,OAAO,WAAW,GAAG;AACvB;AAEA,MAAM,gBAAgB,CAAC,OAAiB,SAAiB,kBAAmC;AAC1F,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AAEzC,MAAI,eAAe;AACV,WAAA,GAAG,WAAW,GAAG,GAAG,SAAS,GAAG,WAAW,KAAK,IACrD,YAAY,KAAK,CACnB,GAAG,MAAM,YAAY,CAAC,GAAG,WAAW,KAAK,IAAI,WAAW,MAAM,aAAa,WAAW,KAAK,IAAI,OAAO;AAAA,EAAA;AAGxG,SAAO,GAAG,SAAS,IAAI,MAAM,aAAa,eAAe,OAAO;AAClE;AAQa,MAAA,eAAe,CAAC,YAAiD;AACtE,QAAA,WAAU,mCAAS,cAAa;AAChC,QAAA,YAAW,mCAAS,UAAS;AAE7B,QAAA,iBAAgB,mCAAS,mBAAkB;AAGjD,QAAM,UAAU,CAAC,OAAiB,SAAiB,OAAc,CAAA,MAAa;AAC5E,QAAI,EAAE,WAAW,iBAAiB,UAAU,KAAK,IAAI;AACnD;AAAA,IAAA;AAGF,UAAM,mBAAmB,cAAc,OAAO,SAAS,aAAa;AAEpE,QAAI,CAAC,WAAW,OAAO,QAAQ,QAAQ,YAAY;AACjD,UAAI,UAAU,SAAS;AACb,gBAAA,MAAM,kBAAkB,GAAG,IAAI;AAAA,MAAA,WAC9B,UAAU,QAAQ;AACnB,gBAAA,KAAK,kBAAkB,GAAG,IAAI;AAAA,MAAA,OACjC;AACG,gBAAA,IAAI,kBAAkB,GAAG,IAAI;AAAA,MAAA;AAEvC;AAAA,IAAA;AAGF,YAAQ,IAAI,UAAU,YAAY,SAAS,OAAO,SAAS,GAAG,IAAI;AAAA,EACpE;AAEA,QAAMA,UAAS,OAAO;AAAA,IACpB,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,SAAY,OAAI,MAAwB,QAAQ,OAAO,SAAS,IAAI,CAAC,CAAC;AAAA,EAC3G;AAEO,SAAA;AAAA,IACL,GAAGA;AAAAA,IACH,IAAI,QAAQ;AACH,aAAA;AAAA,IAAA;AAAA,EAEX;AACF;AAEsB,aAAa;"}
1
+ {"version":3,"file":"logger.js","sources":["../../src/logger.ts"],"sourcesContent":["export const TTY_COLORS = {\n reset: \"\\x1b[0m\",\n bright: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n undim: \"\\x1b[22m\",\n underscore: \"\\x1b[4m\",\n blink: \"\\x1b[5m\",\n reverse: \"\\x1b[7m\",\n hidden: \"\\x1b[8m\",\n fg: {\n black: \"\\x1b[30m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n },\n bg: {\n black: \"\\x1b[40m\",\n red: \"\\x1b[41m\",\n green: \"\\x1b[42m\",\n yellow: \"\\x1b[43m\",\n blue: \"\\x1b[44m\",\n magenta: \"\\x1b[45m\",\n cyan: \"\\x1b[46m\",\n white: \"\\x1b[47m\",\n },\n} as const;\n\nexport type LogLevel = \"debug\" | \"info\" | \"success\" | \"warn\" | \"error\";\n\nexport const levels = [\"debug\", \"info\", \"success\", \"warn\", \"error\"] as const;\n\nexport function shouldPublishLog(currentLogLevel: LogLevel, logLevel: LogLevel): boolean {\n return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);\n}\n\nexport interface Logger {\n disabled?: boolean | undefined;\n disableColors?: boolean | undefined;\n level?: Exclude<LogLevel, \"success\"> | undefined;\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic log arguments from user code\n log?: ((level: Exclude<LogLevel, \"success\">, message: string, ...args: any[]) => void) | undefined;\n}\n\nexport type LogHandlerParams = Parameters<NonNullable<Logger[\"log\"]>> extends [LogLevel, ...infer Rest] ? Rest : never;\n\nconst levelColors: Record<LogLevel, string> = {\n info: TTY_COLORS.fg.blue,\n success: TTY_COLORS.fg.green,\n warn: TTY_COLORS.fg.yellow,\n error: TTY_COLORS.fg.red,\n debug: TTY_COLORS.fg.magenta,\n};\n\nconst formatMessage = (level: LogLevel, message: string, colorsEnabled: boolean): string => {\n const timestamp = new Date().toISOString();\n\n if (colorsEnabled) {\n return `${TTY_COLORS.dim}${timestamp}${TTY_COLORS.reset} ${\n levelColors[level]\n }${level.toUpperCase()}${TTY_COLORS.reset} ${TTY_COLORS.bright}[FMODATA]:${TTY_COLORS.reset} ${message}`;\n }\n\n return `${timestamp} ${level.toUpperCase()} [FMODATA]: ${message}`;\n};\n\nexport type InternalLogger = {\n [K in LogLevel]: (...params: LogHandlerParams) => void;\n} & {\n get level(): LogLevel;\n};\n\nexport const createLogger = (options?: Logger | undefined): InternalLogger => {\n const enabled = options?.disabled !== true;\n const logLevel = options?.level ?? \"error\";\n\n const colorsEnabled = options?.disableColors !== true;\n\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic log arguments from user code\n const LogFunc = (level: LogLevel, message: string, args: any[] = []): void => {\n if (!(enabled && shouldPublishLog(logLevel, level))) {\n return;\n }\n\n const formattedMessage = formatMessage(level, message, colorsEnabled);\n\n if (!options || typeof options.log !== \"function\") {\n if (level === \"error\") {\n console.error(formattedMessage, ...args);\n } else if (level === \"warn\") {\n console.warn(formattedMessage, ...args);\n } else {\n console.log(formattedMessage, ...args);\n }\n return;\n }\n\n options.log(level === \"success\" ? \"info\" : level, message, ...args);\n };\n\n const logger = Object.fromEntries(\n levels.map((level) => [level, (...[message, ...args]: LogHandlerParams) => LogFunc(level, message, args)]),\n ) as Record<LogLevel, (...params: LogHandlerParams) => void>;\n\n return {\n ...logger,\n get level() {\n return logLevel;\n },\n };\n};\n\nexport const logger = createLogger();\n"],"names":["logger"],"mappings":"AAAO,MAAM,aAAa;AAAA,EACxB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EAML,IAAI;AAAA,IAEF,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EAGX;AAWF;AAIO,MAAM,SAAS,CAAC,SAAS,QAAQ,WAAW,QAAQ,OAAO;AAE3D,SAAS,iBAAiB,iBAA2B,UAA6B;AACvF,SAAO,OAAO,QAAQ,QAAQ,KAAK,OAAO,QAAQ,eAAe;AACnE;AAYA,MAAM,cAAwC;AAAA,EAC5C,MAAM,WAAW,GAAG;AAAA,EACpB,SAAS,WAAW,GAAG;AAAA,EACvB,MAAM,WAAW,GAAG;AAAA,EACpB,OAAO,WAAW,GAAG;AAAA,EACrB,OAAO,WAAW,GAAG;AACvB;AAEA,MAAM,gBAAgB,CAAC,OAAiB,SAAiB,kBAAmC;AAC1F,QAAM,aAAY,oBAAI,KAAA,GAAO,YAAA;AAE7B,MAAI,eAAe;AACjB,WAAO,GAAG,WAAW,GAAG,GAAG,SAAS,GAAG,WAAW,KAAK,IACrD,YAAY,KAAK,CACnB,GAAG,MAAM,YAAA,CAAa,GAAG,WAAW,KAAK,IAAI,WAAW,MAAM,aAAa,WAAW,KAAK,IAAI,OAAO;AAAA,EACxG;AAEA,SAAO,GAAG,SAAS,IAAI,MAAM,aAAa,eAAe,OAAO;AAClE;AAQO,MAAM,eAAe,CAAC,YAAiD;AAC5E,QAAM,WAAU,mCAAS,cAAa;AACtC,QAAM,YAAW,mCAAS,UAAS;AAEnC,QAAM,iBAAgB,mCAAS,mBAAkB;AAGjD,QAAM,UAAU,CAAC,OAAiB,SAAiB,OAAc,CAAA,MAAa;AAC5E,QAAI,EAAE,WAAW,iBAAiB,UAAU,KAAK,IAAI;AACnD;AAAA,IACF;AAEA,UAAM,mBAAmB,cAAc,OAAO,SAAS,aAAa;AAEpE,QAAI,CAAC,WAAW,OAAO,QAAQ,QAAQ,YAAY;AACjD,UAAI,UAAU,SAAS;AACrB,gBAAQ,MAAM,kBAAkB,GAAG,IAAI;AAAA,MACzC,WAAW,UAAU,QAAQ;AAC3B,gBAAQ,KAAK,kBAAkB,GAAG,IAAI;AAAA,MACxC,OAAO;AACL,gBAAQ,IAAI,kBAAkB,GAAG,IAAI;AAAA,MACvC;AACA;AAAA,IACF;AAEA,YAAQ,IAAI,UAAU,YAAY,SAAS,OAAO,SAAS,GAAG,IAAI;AAAA,EACpE;AAEA,QAAMA,UAAS,OAAO;AAAA,IACpB,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,SAAY,OAAI,MAAwB,QAAQ,OAAO,SAAS,IAAI,CAAC,CAAC;AAAA,EAAA;AAG3G,SAAO;AAAA,IACL,GAAGA;AAAAA,IACH,IAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;AAEsB,aAAA;"}