@proofkit/fmodata 0.1.0-alpha.4 → 0.1.0-alpha.6
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.
- package/README.md +357 -28
- package/dist/esm/client/base-table.d.ts +122 -5
- package/dist/esm/client/base-table.js +46 -5
- package/dist/esm/client/base-table.js.map +1 -1
- package/dist/esm/client/database.d.ts +20 -3
- package/dist/esm/client/database.js +62 -13
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.js +24 -27
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +9 -6
- package/dist/esm/client/entity-set.js +5 -1
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/filemaker-odata.d.ts +17 -4
- package/dist/esm/client/filemaker-odata.js +90 -27
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.js +45 -34
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query-builder.d.ts +7 -2
- package/dist/esm/client/query-builder.js +273 -202
- package/dist/esm/client/query-builder.js.map +1 -1
- package/dist/esm/client/record-builder.d.ts +2 -2
- package/dist/esm/client/record-builder.js +50 -40
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/table-occurrence.d.ts +66 -2
- package/dist/esm/client/table-occurrence.js +36 -1
- package/dist/esm/client/table-occurrence.js.map +1 -1
- package/dist/esm/client/update-builder.js +39 -35
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/errors.d.ts +60 -0
- package/dist/esm/errors.js +122 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +5 -2
- package/dist/esm/index.js +25 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/transform.d.ts +56 -0
- package/dist/esm/transform.js +107 -0
- package/dist/esm/transform.js.map +1 -0
- package/dist/esm/types.d.ts +21 -5
- package/dist/esm/validation.d.ts +6 -3
- package/dist/esm/validation.js +104 -33
- package/dist/esm/validation.js.map +1 -1
- package/package.json +10 -1
- package/src/client/base-table.ts +155 -8
- package/src/client/database.ts +116 -13
- package/src/client/delete-builder.ts +42 -43
- package/src/client/entity-set.ts +21 -11
- package/src/client/filemaker-odata.ts +132 -34
- package/src/client/insert-builder.ts +69 -37
- package/src/client/query-builder.ts +345 -233
- package/src/client/record-builder.ts +84 -59
- package/src/client/table-occurrence.ts +118 -4
- package/src/client/update-builder.ts +77 -49
- package/src/errors.ts +185 -0
- package/src/index.ts +30 -1
- package/src/transform.ts +236 -0
- package/src/types.ts +112 -34
- package/src/validation.ts +120 -36
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-occurrence.js","sources":["../../../src/client/table-occurrence.ts"],"sourcesContent":["import { BaseTable } from \"./base-table\";\n\n// Helper type to extract schema from BaseTable\ntype ExtractSchema<BT> =\n BT extends BaseTable<infer S, any, any, any> ? S : never;\n\n// Helper type to resolve navigation functions to their return types\ntype ResolveNavigation<T> = {\n [K in keyof T]: T[K] extends () => infer R ? R : T[K];\n};\n\n// Helper to create a getter-based navigation object\nfunction createNavigationGetters<\n Nav extends Record<\n string,\n | TableOccurrence<any, any, any, any>\n | (() => TableOccurrence<any, any, any, any>)\n >,\n>(navConfig: Nav): ResolveNavigation<Nav> {\n const result: any = {};\n\n for (const key in navConfig) {\n Object.defineProperty(result, key, {\n get() {\n const navItem = navConfig[key];\n return typeof navItem === \"function\" ? navItem() : navItem;\n },\n enumerable: true,\n configurable: true,\n });\n }\n\n return result as ResolveNavigation<Nav>;\n}\n\nexport class TableOccurrence<\n BT extends BaseTable<any, any, any, any> = any,\n Name extends string = string,\n Nav extends Record<\n string,\n | TableOccurrence<any, any, any, any>\n | (() => TableOccurrence<any, any, any, any>)\n > = {},\n DefSelect extends\n | \"all\"\n | \"schema\"\n | readonly (keyof ExtractSchema<BT>)[] = \"schema\",\n> {\n public readonly name: Name;\n public readonly baseTable: BT;\n
|
|
1
|
+
{"version":3,"file":"table-occurrence.js","sources":["../../../src/client/table-occurrence.ts"],"sourcesContent":["import { BaseTable, BaseTableWithIds } from \"./base-table\";\n\n// Helper type to extract schema from BaseTable\ntype ExtractSchema<BT> =\n BT extends BaseTable<infer S, any, any, any> ? S : never;\n\n// Helper type to resolve navigation functions to their return types\ntype ResolveNavigation<T> = {\n [K in keyof T]: T[K] extends () => infer R ? R : T[K];\n};\n\n// Helper to create a getter-based navigation object\nfunction createNavigationGetters<\n Nav extends Record<\n string,\n | TableOccurrence<any, any, any, any>\n | (() => TableOccurrence<any, any, any, any>)\n >,\n>(navConfig: Nav): ResolveNavigation<Nav> {\n const result: any = {};\n\n for (const key in navConfig) {\n Object.defineProperty(result, key, {\n get() {\n const navItem = navConfig[key];\n return typeof navItem === \"function\" ? navItem() : navItem;\n },\n enumerable: true,\n configurable: true,\n });\n }\n\n return result as ResolveNavigation<Nav>;\n}\n\nexport class TableOccurrence<\n BT extends BaseTable<any, any, any, any> = any,\n Name extends string = string,\n Nav extends Record<\n string,\n | TableOccurrence<any, any, any, any>\n | (() => TableOccurrence<any, any, any, any>)\n > = {},\n DefSelect extends\n | \"all\"\n | \"schema\"\n | readonly (keyof ExtractSchema<BT>)[] = \"schema\",\n> {\n public readonly name: Name;\n public readonly baseTable: BT;\n protected _navigationConfig: Nav;\n public readonly navigation: ResolveNavigation<Nav>;\n public readonly defaultSelect: DefSelect;\n constructor(config: {\n readonly name: Name;\n readonly baseTable: BT;\n readonly navigation?: Nav;\n readonly defaultSelect?: DefSelect;\n }) {\n this.name = config.name;\n this.baseTable = config.baseTable;\n this._navigationConfig = (config.navigation ?? {}) as Nav;\n this.defaultSelect = (config.defaultSelect ?? \"schema\") as DefSelect;\n // Create navigation getters that lazily resolve functions\n this.navigation = createNavigationGetters(this._navigationConfig);\n }\n\n addNavigation<\n NewNav extends Record<\n string,\n | TableOccurrence<any, any, any, any>\n | (() => TableOccurrence<any, any, any, any>)\n >,\n >(nav: NewNav): TableOccurrence<BT, Name, Nav & NewNav, DefSelect> {\n return new TableOccurrence({\n name: this.name,\n baseTable: this.baseTable,\n navigation: { ...this._navigationConfig, ...nav } as Nav & NewNav,\n defaultSelect: this.defaultSelect,\n });\n }\n\n /**\n * Returns the FileMaker table occurrence ID (FMTID) if available, or the table name.\n * @returns The FMTID string or the table name\n */\n getTableId(): string {\n // Check if fmtId exists (only on TableOccurrenceWithIds)\n return \"fmtId\" in this && (this as any).fmtId\n ? (this as any).fmtId\n : this.name;\n }\n\n /**\n * Returns the table occurrence name.\n * @returns The table name\n */\n getTableName(): string {\n return this.name;\n }\n\n /**\n * Returns true if this TableOccurrence is using FileMaker table occurrence IDs.\n */\n isUsingTableId(): boolean {\n return \"fmtId\" in this && this.fmtId !== undefined;\n }\n}\n\n// Helper function to create TableOccurrence with proper type inference\nexport function createTableOccurrence<\n const Name extends string,\n BT extends BaseTable<any, any, any, any>,\n DefSelect extends\n | \"all\"\n | \"schema\"\n | readonly (keyof ExtractSchema<BT>)[] = \"schema\",\n>(config: {\n name: Name;\n baseTable: BT;\n defaultSelect?: DefSelect;\n}): TableOccurrence<BT, Name, {}, DefSelect> {\n return new TableOccurrence(config);\n}\n\n// Helper type to validate that all navigation values are TableOccurrenceWithIds\ntype ValidateNavWithIds<Nav> =\n Nav extends Record<\n string,\n | TableOccurrenceWithIds<any, any, any, any>\n | (() => TableOccurrenceWithIds<any, any, any, any>)\n >\n ? Nav\n : \"Error: All navigation table occurrences must be TableOccurrenceWithIds when using TableOccurrenceWithIds\";\n\n/**\n * TableOccurrenceWithIds extends TableOccurrence to require:\n * 1. A BaseTableWithIds (which has fmfIds defined)\n * 2. A required fmtId for this table occurrence\n * 3. All navigation relationships must also be TableOccurrenceWithIds\n *\n * This ensures bidirectional type-level enforcement: if you use FileMaker IDs,\n * they must be defined on both the BaseTable (fmfIds) and TableOccurrence (fmtId),\n * and all related table occurrences in navigation must also have IDs.\n *\n * @template BT - Must be a BaseTableWithIds (enforced at type level)\n * @template Name - The name of this table occurrence\n * @template Nav - Navigation relationships (must all be TableOccurrenceWithIds)\n * @template DefSelect - Default select behavior\n *\n * @example\n * ```ts\n * const usersBaseWithIds = new BaseTableWithIds({\n * schema: { id: z.string(), name: z.string() },\n * idField: \"id\",\n * fmfIds: { id: \"FMFID:1\", name: \"FMFID:2\" },\n * });\n *\n * const usersTO = new TableOccurrenceWithIds({\n * name: \"users\",\n * baseTable: usersBaseWithIds,\n * fmtId: \"FMTID:100\",\n * navigation: {\n * contacts: () => contactsTO, // Must also be TableOccurrenceWithIds\n * },\n * });\n * ```\n */\nexport class TableOccurrenceWithIds<\n BT extends BaseTableWithIds<any, any, any, any> = any,\n Name extends string = string,\n Nav extends Record<\n string,\n | TableOccurrence<any, any, any, any>\n | (() => TableOccurrence<any, any, any, any>)\n > = {},\n DefSelect extends\n | \"all\"\n | \"schema\"\n | readonly (keyof ExtractSchema<BT>)[] = \"schema\",\n> extends TableOccurrence<BT, Name, Nav, DefSelect> {\n public readonly fmtId: `FMTID:${string}`;\n\n constructor(config: {\n readonly name: Name;\n readonly baseTable: BT;\n readonly fmtId: `FMTID:${string}`;\n readonly navigation?: ValidateNavWithIds<Nav>;\n readonly defaultSelect?: DefSelect;\n }) {\n super({\n ...config,\n navigation: config.navigation as Nav,\n });\n this.fmtId = config.fmtId;\n }\n}\n\n// Helper function to create TableOccurrenceWithIds with proper type inference\nexport function createTableOccurrenceWithIds<\n const Name extends string,\n BT extends BaseTableWithIds<any, any, any, any>,\n DefSelect extends\n | \"all\"\n | \"schema\"\n | readonly (keyof ExtractSchema<BT>)[] = \"schema\",\n>(config: {\n name: Name;\n baseTable: BT;\n fmtId: `FMTID:${string}`;\n defaultSelect?: DefSelect;\n}): TableOccurrenceWithIds<BT, Name, {}, DefSelect> {\n return new TableOccurrenceWithIds(config);\n}\n"],"names":[],"mappings":";;;AAYA,SAAS,wBAMP,WAAwC;AACxC,QAAM,SAAc,CAAC;AAErB,aAAW,OAAO,WAAW;AACpB,WAAA,eAAe,QAAQ,KAAK;AAAA,MACjC,MAAM;AACE,cAAA,UAAU,UAAU,GAAG;AAC7B,eAAO,OAAO,YAAY,aAAa,QAAY,IAAA;AAAA,MACrD;AAAA,MACA,YAAY;AAAA,MACZ,cAAc;AAAA,IAAA,CACf;AAAA,EAAA;AAGI,SAAA;AACT;AAEO,MAAM,gBAYX;AAAA,EAMA,YAAY,QAKT;AAVa;AACA;AACN;AACM;AACA;AAOd,SAAK,OAAO,OAAO;AACnB,SAAK,YAAY,OAAO;AACnB,SAAA,oBAAqB,OAAO,cAAc,CAAC;AAC3C,SAAA,gBAAiB,OAAO,iBAAiB;AAEzC,SAAA,aAAa,wBAAwB,KAAK,iBAAiB;AAAA,EAAA;AAAA,EAGlE,cAME,KAAiE;AACjE,WAAO,IAAI,gBAAgB;AAAA,MACzB,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,YAAY,EAAE,GAAG,KAAK,mBAAmB,GAAG,IAAI;AAAA,MAChD,eAAe,KAAK;AAAA,IAAA,CACrB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOH,aAAqB;AAEnB,WAAO,WAAW,QAAS,KAAa,QACnC,KAAa,QACd,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOX,eAAuB;AACrB,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,iBAA0B;AACjB,WAAA,WAAW,QAAQ,KAAK,UAAU;AAAA,EAAA;AAE7C;AAGO,SAAS,sBAOd,QAI2C;AACpC,SAAA,IAAI,gBAAgB,MAAM;AACnC;AA6CO,MAAM,+BAYH,gBAA0C;AAAA,EAGlD,YAAY,QAMT;AACK,UAAA;AAAA,MACJ,GAAG;AAAA,MACH,YAAY,OAAO;AAAA,IAAA,CACpB;AAZa;AAad,SAAK,QAAQ,OAAO;AAAA,EAAA;AAExB;AAGO,SAAS,6BAOd,QAKkD;AAC3C,SAAA,IAAI,uBAAuB,MAAM;AAC1C;"}
|
|
@@ -2,6 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { QueryBuilder } from "./query-builder.js";
|
|
5
|
+
import { transformTableName, transformFieldNamesToIds } from "../transform.js";
|
|
5
6
|
class UpdateBuilder {
|
|
6
7
|
constructor(config) {
|
|
7
8
|
__publicField(this, "tableName");
|
|
@@ -74,56 +75,59 @@ class ExecutableUpdateBuilder {
|
|
|
74
75
|
this.queryBuilder = config.queryBuilder;
|
|
75
76
|
}
|
|
76
77
|
async execute(options) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const queryParams = queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
|
|
87
|
-
url = `/${this.databaseName}/${this.tableName}${queryParams}`;
|
|
88
|
-
}
|
|
89
|
-
const response = await this.context._makeRequest(url, {
|
|
90
|
-
method: "PATCH",
|
|
91
|
-
headers: {
|
|
92
|
-
"Content-Type": "application/json"
|
|
93
|
-
},
|
|
94
|
-
body: JSON.stringify(this.data),
|
|
95
|
-
...options
|
|
96
|
-
});
|
|
97
|
-
let updatedCount = 0;
|
|
98
|
-
if (typeof response === "number") {
|
|
99
|
-
updatedCount = response;
|
|
100
|
-
} else if (response && typeof response === "object") {
|
|
101
|
-
updatedCount = response.updatedCount || 0;
|
|
78
|
+
var _a;
|
|
79
|
+
const tableId = this.occurrence ? transformTableName(this.occurrence) : this.tableName;
|
|
80
|
+
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
81
|
+
let url;
|
|
82
|
+
if (this.mode === "byId") {
|
|
83
|
+
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
84
|
+
} else {
|
|
85
|
+
if (!this.queryBuilder) {
|
|
86
|
+
throw new Error("Query builder is required for filter-based update");
|
|
102
87
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
88
|
+
const queryString = this.queryBuilder.getQueryString();
|
|
89
|
+
const queryParams = queryString.startsWith(`/${tableId}`) ? queryString.slice(`/${tableId}`.length) : queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
|
|
90
|
+
url = `/${this.databaseName}/${tableId}${queryParams}`;
|
|
91
|
+
}
|
|
92
|
+
const result = await this.context._makeRequest(url, {
|
|
93
|
+
method: "PATCH",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json"
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify(transformedData),
|
|
98
|
+
...options
|
|
99
|
+
});
|
|
100
|
+
if (result.error) {
|
|
101
|
+
return { data: void 0, error: result.error };
|
|
102
|
+
}
|
|
103
|
+
const response = result.data;
|
|
104
|
+
let updatedCount = 0;
|
|
105
|
+
if (typeof response === "number") {
|
|
106
|
+
updatedCount = response;
|
|
107
|
+
} else if (response && typeof response === "object") {
|
|
108
|
+
updatedCount = response.updatedCount || 0;
|
|
109
109
|
}
|
|
110
|
+
return { data: { updatedCount }, error: void 0 };
|
|
110
111
|
}
|
|
111
112
|
getRequestConfig() {
|
|
113
|
+
var _a;
|
|
114
|
+
const tableId = this.occurrence ? transformTableName(this.occurrence) : this.tableName;
|
|
115
|
+
const transformedData = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) ? transformFieldNamesToIds(this.data, this.occurrence.baseTable) : this.data;
|
|
112
116
|
let url;
|
|
113
117
|
if (this.mode === "byId") {
|
|
114
|
-
url = `/${this.databaseName}/${
|
|
118
|
+
url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
|
|
115
119
|
} else {
|
|
116
120
|
if (!this.queryBuilder) {
|
|
117
121
|
throw new Error("Query builder is required for filter-based update");
|
|
118
122
|
}
|
|
119
123
|
const queryString = this.queryBuilder.getQueryString();
|
|
120
|
-
const queryParams = queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
|
|
121
|
-
url = `/${this.databaseName}/${
|
|
124
|
+
const queryParams = queryString.startsWith(`/${tableId}`) ? queryString.slice(`/${tableId}`.length) : queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
|
|
125
|
+
url = `/${this.databaseName}/${tableId}${queryParams}`;
|
|
122
126
|
}
|
|
123
127
|
return {
|
|
124
128
|
method: "PATCH",
|
|
125
129
|
url,
|
|
126
|
-
body: JSON.stringify(
|
|
130
|
+
body: JSON.stringify(transformedData)
|
|
127
131
|
};
|
|
128
132
|
}
|
|
129
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport type { BaseTable } from \"./base-table\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\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 T extends Record<string, any>,\n BT extends BaseTable<any, any, any, any>,\n> {\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n }\n\n /**\n * Update a single record by ID\n * Returns the count of updated records (0 or 1)\n */\n byId(id: string | number): ExecutableUpdateBuilder<T, true> {\n return new ExecutableUpdateBuilder<T, true>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns the count of updated records\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(\n fn: (\n q: QueryBuilder<WithSystemFields<T>>,\n ) => QueryBuilder<WithSystemFields<T>>,\n ): ExecutableUpdateBuilder<T, true> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<\n WithSystemFields<T>,\n keyof WithSystemFields<T>,\n false,\n false,\n undefined\n >({\n occurrence: undefined,\n tableName: this.tableName,\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<T, true>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Both modes return the count of updated records\n */\nexport class ExecutableUpdateBuilder<\n T extends Record<string, any>,\n IsByFilter extends boolean,\n> implements ExecutableBuilder<{ updatedCount: number }>\n{\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<any>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<any>;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\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 }\n\n async execute(\n options?: RequestInit & FFetchOptions,\n ): Promise<Result<{ updatedCount: number }>> {\n try {\n let url: string;\n\n if (this.mode === \"byId\") {\n // Update single record by ID: PATCH /{database}/{table}('id')\n url = `/${this.databaseName}/${this.tableName}('${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 // Remove the leading \"/\" from the query string as we'll build our own URL\n const queryParams = queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${this.tableName}${queryParams}`;\n }\n\n // Make PATCH request with JSON body\n const response = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(this.data),\n ...options,\n });\n\n // Both byId and byFilter return affected row count\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 updatedCount = (response as any).updatedCount || 0;\n }\n\n return { data: { updatedCount }, error: undefined };\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n let url: string;\n\n if (this.mode === \"byId\") {\n url = `/${this.databaseName}/${this.tableName}('${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 queryParams = queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${this.tableName}${queryParams}`;\n }\n\n return {\n method: \"PATCH\",\n url,\n body: JSON.stringify(this.data),\n };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update-builder.js","sources":["../../../src/client/update-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport type { BaseTable } from \"./base-table\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport {\n transformFieldNamesToIds,\n transformTableName,\n} from \"../transform\";\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 T extends Record<string, any>,\n BT extends BaseTable<any, any, any, any>,\n> {\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n }\n\n /**\n * Update a single record by ID\n * Returns the count of updated records (0 or 1)\n */\n byId(id: string | number): ExecutableUpdateBuilder<T, true> {\n return new ExecutableUpdateBuilder<T, true>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byId\",\n recordId: id,\n });\n }\n\n /**\n * Update records matching a filter query\n * Returns the count of updated records\n * @param fn Callback that receives a QueryBuilder for building the filter\n */\n where(\n fn: (\n q: QueryBuilder<WithSystemFields<T>>,\n ) => QueryBuilder<WithSystemFields<T>>,\n ): ExecutableUpdateBuilder<T, true> {\n // Create a QueryBuilder for the user to configure\n const queryBuilder = new QueryBuilder<\n WithSystemFields<T>,\n keyof WithSystemFields<T>,\n false,\n false,\n undefined\n >({\n occurrence: undefined,\n tableName: this.tableName,\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<T, true>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: this.data,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n });\n }\n}\n\n/**\n * Executable update builder - has execute() method\n * Returned after calling .byId() or .where()\n * Both modes return the count of updated records\n */\nexport class ExecutableUpdateBuilder<\n T extends Record<string, any>,\n IsByFilter extends boolean,\n> implements ExecutableBuilder<{ updatedCount: number }>\n{\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private data: Partial<T>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<any>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<any>;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\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 }\n\n async execute(\n options?: RequestInit & FFetchOptions,\n ): Promise<Result<{ updatedCount: number }>> {\n // Transform table name to FMTID if using entity IDs\n const tableId = this.occurrence\n ? transformTableName(this.occurrence)\n : this.tableName;\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData = this.occurrence?.baseTable\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : this.data;\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 queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\n\n url = `/${this.databaseName}/${tableId}${queryParams}`;\n }\n\n // Make PATCH request with JSON body\n const result = await this.context._makeRequest(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(transformedData),\n ...options,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // Both byId and byFilter return affected row count\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 updatedCount = (response as any).updatedCount || 0;\n }\n\n return { data: { updatedCount }, error: undefined };\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n // Transform table name to FMTID if using entity IDs\n const tableId = this.occurrence\n ? transformTableName(this.occurrence)\n : this.tableName;\n\n // Transform field names to FMFIDs if using entity IDs\n const transformedData = this.occurrence?.baseTable\n ? transformFieldNamesToIds(this.data, this.occurrence.baseTable)\n : 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 queryParams = queryString.startsWith(`/${tableId}`)\n ? queryString.slice(`/${tableId}`.length)\n : queryString.startsWith(`/${this.tableName}`)\n ? queryString.slice(`/${this.tableName}`.length)\n : queryString;\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"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
/**
|
|
3
|
+
* Base class for all fmodata errors
|
|
4
|
+
*/
|
|
5
|
+
export declare abstract class FMODataError extends Error {
|
|
6
|
+
abstract readonly kind: string;
|
|
7
|
+
readonly timestamp: Date;
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
export declare class HTTPError extends FMODataError {
|
|
11
|
+
readonly kind: "HTTPError";
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly status: number;
|
|
14
|
+
readonly statusText: string;
|
|
15
|
+
readonly response?: any;
|
|
16
|
+
constructor(url: string, status: number, statusText: string, response?: any);
|
|
17
|
+
is4xx(): boolean;
|
|
18
|
+
is5xx(): boolean;
|
|
19
|
+
isNotFound(): boolean;
|
|
20
|
+
isUnauthorized(): boolean;
|
|
21
|
+
isForbidden(): boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare class ODataError extends FMODataError {
|
|
24
|
+
readonly kind: "ODataError";
|
|
25
|
+
readonly url: string;
|
|
26
|
+
readonly code?: string;
|
|
27
|
+
readonly details?: any;
|
|
28
|
+
constructor(url: string, message: string, code?: string, details?: any);
|
|
29
|
+
}
|
|
30
|
+
export declare class ValidationError extends FMODataError {
|
|
31
|
+
readonly kind: "ValidationError";
|
|
32
|
+
readonly field?: string;
|
|
33
|
+
readonly issues: readonly StandardSchemaV1.Issue[];
|
|
34
|
+
readonly value?: unknown;
|
|
35
|
+
constructor(message: string, issues: readonly StandardSchemaV1.Issue[], options?: {
|
|
36
|
+
field?: string;
|
|
37
|
+
value?: unknown;
|
|
38
|
+
cause?: Error["cause"];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export declare class ResponseStructureError extends FMODataError {
|
|
42
|
+
readonly kind: "ResponseStructureError";
|
|
43
|
+
readonly expected: string;
|
|
44
|
+
readonly received: any;
|
|
45
|
+
constructor(expected: string, received: any);
|
|
46
|
+
}
|
|
47
|
+
export declare class RecordCountMismatchError extends FMODataError {
|
|
48
|
+
readonly kind: "RecordCountMismatchError";
|
|
49
|
+
readonly expected: number | "one" | "at-most-one";
|
|
50
|
+
readonly received: number;
|
|
51
|
+
constructor(expected: number | "one" | "at-most-one", received: number);
|
|
52
|
+
}
|
|
53
|
+
export declare function isHTTPError(error: unknown): error is HTTPError;
|
|
54
|
+
export declare function isValidationError(error: unknown): error is ValidationError;
|
|
55
|
+
export declare function isODataError(error: unknown): error is ODataError;
|
|
56
|
+
export declare function isResponseStructureError(error: unknown): error is ResponseStructureError;
|
|
57
|
+
export declare function isRecordCountMismatchError(error: unknown): error is RecordCountMismatchError;
|
|
58
|
+
export declare function isFMODataError(error: unknown): error is FMODataError;
|
|
59
|
+
export type { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError, } from '@fetchkit/ffetch';
|
|
60
|
+
export type FMODataErrorType = import('@fetchkit/ffetch').TimeoutError | import('@fetchkit/ffetch').AbortError | import('@fetchkit/ffetch').NetworkError | import('@fetchkit/ffetch').RetryLimitError | import('@fetchkit/ffetch').CircuitOpenError | HTTPError | ODataError | ValidationError | ResponseStructureError | RecordCountMismatchError;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
class FMODataError extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
__publicField(this, "timestamp");
|
|
8
|
+
this.name = this.constructor.name;
|
|
9
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
class HTTPError extends FMODataError {
|
|
13
|
+
constructor(url, status, statusText, response) {
|
|
14
|
+
super(`HTTP ${status} ${statusText} for ${url}`);
|
|
15
|
+
__publicField(this, "kind", "HTTPError");
|
|
16
|
+
__publicField(this, "url");
|
|
17
|
+
__publicField(this, "status");
|
|
18
|
+
__publicField(this, "statusText");
|
|
19
|
+
__publicField(this, "response");
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.status = status;
|
|
22
|
+
this.statusText = statusText;
|
|
23
|
+
this.response = response;
|
|
24
|
+
}
|
|
25
|
+
// Helper methods for common status checks
|
|
26
|
+
is4xx() {
|
|
27
|
+
return this.status >= 400 && this.status < 500;
|
|
28
|
+
}
|
|
29
|
+
is5xx() {
|
|
30
|
+
return this.status >= 500 && this.status < 600;
|
|
31
|
+
}
|
|
32
|
+
isNotFound() {
|
|
33
|
+
return this.status === 404;
|
|
34
|
+
}
|
|
35
|
+
isUnauthorized() {
|
|
36
|
+
return this.status === 401;
|
|
37
|
+
}
|
|
38
|
+
isForbidden() {
|
|
39
|
+
return this.status === 403;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
class ODataError extends FMODataError {
|
|
43
|
+
constructor(url, message, code, details) {
|
|
44
|
+
super(`OData error: ${message}`);
|
|
45
|
+
__publicField(this, "kind", "ODataError");
|
|
46
|
+
__publicField(this, "url");
|
|
47
|
+
__publicField(this, "code");
|
|
48
|
+
__publicField(this, "details");
|
|
49
|
+
this.url = url;
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.details = details;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class ValidationError extends FMODataError {
|
|
55
|
+
constructor(message, issues, options) {
|
|
56
|
+
super(
|
|
57
|
+
message,
|
|
58
|
+
(options == null ? void 0 : options.cause) !== void 0 ? { cause: options.cause } : void 0
|
|
59
|
+
);
|
|
60
|
+
__publicField(this, "kind", "ValidationError");
|
|
61
|
+
__publicField(this, "field");
|
|
62
|
+
__publicField(this, "issues");
|
|
63
|
+
__publicField(this, "value");
|
|
64
|
+
this.field = options == null ? void 0 : options.field;
|
|
65
|
+
this.issues = issues;
|
|
66
|
+
this.value = options == null ? void 0 : options.value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class ResponseStructureError extends FMODataError {
|
|
70
|
+
constructor(expected, received) {
|
|
71
|
+
super(`Invalid response structure: expected ${expected}`);
|
|
72
|
+
__publicField(this, "kind", "ResponseStructureError");
|
|
73
|
+
__publicField(this, "expected");
|
|
74
|
+
__publicField(this, "received");
|
|
75
|
+
this.expected = expected;
|
|
76
|
+
this.received = received;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class RecordCountMismatchError extends FMODataError {
|
|
80
|
+
constructor(expected, received) {
|
|
81
|
+
const expectedStr = typeof expected === "number" ? expected : expected;
|
|
82
|
+
super(`Expected ${expectedStr} record(s), but received ${received}`);
|
|
83
|
+
__publicField(this, "kind", "RecordCountMismatchError");
|
|
84
|
+
__publicField(this, "expected");
|
|
85
|
+
__publicField(this, "received");
|
|
86
|
+
this.expected = expected;
|
|
87
|
+
this.received = received;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function isHTTPError(error) {
|
|
91
|
+
return error instanceof HTTPError;
|
|
92
|
+
}
|
|
93
|
+
function isValidationError(error) {
|
|
94
|
+
return error instanceof ValidationError;
|
|
95
|
+
}
|
|
96
|
+
function isODataError(error) {
|
|
97
|
+
return error instanceof ODataError;
|
|
98
|
+
}
|
|
99
|
+
function isResponseStructureError(error) {
|
|
100
|
+
return error instanceof ResponseStructureError;
|
|
101
|
+
}
|
|
102
|
+
function isRecordCountMismatchError(error) {
|
|
103
|
+
return error instanceof RecordCountMismatchError;
|
|
104
|
+
}
|
|
105
|
+
function isFMODataError(error) {
|
|
106
|
+
return error instanceof FMODataError;
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
FMODataError,
|
|
110
|
+
HTTPError,
|
|
111
|
+
ODataError,
|
|
112
|
+
RecordCountMismatchError,
|
|
113
|
+
ResponseStructureError,
|
|
114
|
+
ValidationError,
|
|
115
|
+
isFMODataError,
|
|
116
|
+
isHTTPError,
|
|
117
|
+
isODataError,
|
|
118
|
+
isRecordCountMismatchError,
|
|
119
|
+
isResponseStructureError,
|
|
120
|
+
isValidationError
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +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 readonly response?: any;\n\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 readonly details?: any;\n\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\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(\n message,\n options?.cause !== undefined ? { cause: options.cause } : undefined,\n );\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 readonly received: any;\n\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\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 isResponseStructureError(\n error: unknown,\n): error is ResponseStructureError {\n return error instanceof ResponseStructureError;\n}\n\nexport function isRecordCountMismatchError(\n error: unknown,\n): error is RecordCountMismatchError {\n return error instanceof RecordCountMismatchError;\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 TimeoutError,\n AbortError,\n NetworkError,\n RetryLimitError,\n CircuitOpenError,\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 | ValidationError\n | ResponseStructureError\n | RecordCountMismatchError;\n"],"names":[],"mappings":";;;AAKO,MAAe,qBAAqB,MAAM;AAAA,EAI/C,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AAHf;AAIF,SAAA,OAAO,KAAK,YAAY;AACxB,SAAA,gCAAgB,KAAK;AAAA,EAAA;AAE9B;AAMO,MAAM,kBAAkB,aAAa;AAAA,EAO1C,YAAY,KAAa,QAAgB,YAAoB,UAAgB;AAC3E,UAAM,QAAQ,MAAM,IAAI,UAAU,QAAQ,GAAG,EAAE;AAPxC,gCAAO;AACP;AACA;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAAA;AAAA;AAAA,EAIlB,QAAiB;AACf,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS;AAAA,EAAA;AAAA,EAG7C,QAAiB;AACf,WAAO,KAAK,UAAU,OAAO,KAAK,SAAS;AAAA,EAAA;AAAA,EAG7C,aAAsB;AACpB,WAAO,KAAK,WAAW;AAAA,EAAA;AAAA,EAGzB,iBAA0B;AACxB,WAAO,KAAK,WAAW;AAAA,EAAA;AAAA,EAGzB,cAAuB;AACrB,WAAO,KAAK,WAAW;AAAA,EAAA;AAE3B;AAMO,MAAM,mBAAmB,aAAa;AAAA,EAM3C,YAAY,KAAa,SAAiB,MAAe,SAAe;AAChE,UAAA,gBAAgB,OAAO,EAAE;AANxB,gCAAO;AACP;AACA;AACA;AAIP,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EAAA;AAEnB;AAMO,MAAM,wBAAwB,aAAa;AAAA,EAMhD,YACE,SACA,QACA,SAKA;AACA;AAAA,MACE;AAAA,OACA,mCAAS,WAAU,SAAY,EAAE,OAAO,QAAQ,UAAU;AAAA,IAC5D;AAjBO,gCAAO;AACP;AACA;AACA;AAeP,SAAK,QAAQ,mCAAS;AACtB,SAAK,SAAS;AACd,SAAK,QAAQ,mCAAS;AAAA,EAAA;AAE1B;AAEO,MAAM,+BAA+B,aAAa;AAAA,EAKvD,YAAY,UAAkB,UAAe;AACrC,UAAA,wCAAwC,QAAQ,EAAE;AALjD,gCAAO;AACP;AACA;AAIP,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAAA;AAEpB;AAEO,MAAM,iCAAiC,aAAa;AAAA,EAKzD,YAAY,UAA0C,UAAkB;AACtE,UAAM,cAAc,OAAO,aAAa,WAAW,WAAW;AAC9D,UAAM,YAAY,WAAW,4BAA4B,QAAQ,EAAE;AAN5D,gCAAO;AACP;AACA;AAKP,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAAA;AAEpB;AAMO,SAAS,YAAY,OAAoC;AAC9D,SAAO,iBAAiB;AAC1B;AAEO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,iBAAiB;AAC1B;AAEO,SAAS,aAAa,OAAqC;AAChE,SAAO,iBAAiB;AAC1B;AAEO,SAAS,yBACd,OACiC;AACjC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,2BACd,OACmC;AACnC,SAAO,iBAAiB;AAC1B;AAEO,SAAS,eAAe,OAAuC;AACpE,SAAO,iBAAiB;AAC1B;"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
export { BaseTable } from './client/base-table.js';
|
|
2
|
-
export { TableOccurrence, createTableOccurrence, } from './client/table-occurrence.js';
|
|
1
|
+
export { BaseTable, BaseTableWithIds } from './client/base-table.js';
|
|
2
|
+
export { TableOccurrence, createTableOccurrence, TableOccurrenceWithIds, createTableOccurrenceWithIds, } from './client/table-occurrence.js';
|
|
3
3
|
export { FMServerConnection } from './client/filemaker-odata.js';
|
|
4
4
|
export type { Database } from './client/database.js';
|
|
5
5
|
export type { EntitySet } from './client/entity-set.js';
|
|
6
6
|
export type { Result, InferSchemaType, InsertData, UpdateData, ODataRecordMetadata, } from './types.js';
|
|
7
7
|
export type { Filter, TypedFilter, FieldFilter, StringOperators, NumberOperators, BooleanOperators, DateOperators, LogicalFilter, } from './filter-types.js';
|
|
8
|
+
export { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError, } from '@fetchkit/ffetch';
|
|
9
|
+
export { FMODataError, HTTPError, ODataError, ValidationError, ResponseStructureError, RecordCountMismatchError, isHTTPError, isValidationError, isODataError, isResponseStructureError, isRecordCountMismatchError, isFMODataError, } from './errors.js';
|
|
10
|
+
export type { FMODataErrorType } from './errors.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
|
-
import { BaseTable } from "./client/base-table.js";
|
|
2
|
-
import { TableOccurrence, createTableOccurrence } from "./client/table-occurrence.js";
|
|
1
|
+
import { BaseTable, BaseTableWithIds } from "./client/base-table.js";
|
|
2
|
+
import { TableOccurrence, TableOccurrenceWithIds, createTableOccurrence, createTableOccurrenceWithIds } from "./client/table-occurrence.js";
|
|
3
3
|
import { FMServerConnection } from "./client/filemaker-odata.js";
|
|
4
|
+
import { AbortError, CircuitOpenError, NetworkError, RetryLimitError, TimeoutError } from "@fetchkit/ffetch";
|
|
5
|
+
import { FMODataError, HTTPError, ODataError, RecordCountMismatchError, ResponseStructureError, ValidationError, isFMODataError, isHTTPError, isODataError, isRecordCountMismatchError, isResponseStructureError, isValidationError } from "./errors.js";
|
|
4
6
|
export {
|
|
7
|
+
AbortError,
|
|
5
8
|
BaseTable,
|
|
9
|
+
BaseTableWithIds,
|
|
10
|
+
CircuitOpenError,
|
|
11
|
+
FMODataError,
|
|
6
12
|
FMServerConnection,
|
|
13
|
+
HTTPError,
|
|
14
|
+
NetworkError,
|
|
15
|
+
ODataError,
|
|
16
|
+
RecordCountMismatchError,
|
|
17
|
+
ResponseStructureError,
|
|
18
|
+
RetryLimitError,
|
|
7
19
|
TableOccurrence,
|
|
8
|
-
|
|
20
|
+
TableOccurrenceWithIds,
|
|
21
|
+
TimeoutError,
|
|
22
|
+
ValidationError,
|
|
23
|
+
createTableOccurrence,
|
|
24
|
+
createTableOccurrenceWithIds,
|
|
25
|
+
isFMODataError,
|
|
26
|
+
isHTTPError,
|
|
27
|
+
isODataError,
|
|
28
|
+
isRecordCountMismatchError,
|
|
29
|
+
isResponseStructureError,
|
|
30
|
+
isValidationError
|
|
9
31
|
};
|
|
10
32
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { BaseTable } from './client/base-table.js';
|
|
2
|
+
import { TableOccurrence } from './client/table-occurrence.js';
|
|
3
|
+
/**
|
|
4
|
+
* Transforms field names to FileMaker field IDs (FMFID) in an object
|
|
5
|
+
* @param data - Object with field names as keys
|
|
6
|
+
* @param baseTable - BaseTable instance to get field IDs from
|
|
7
|
+
* @returns Object with FMFID keys instead of field names
|
|
8
|
+
*/
|
|
9
|
+
export declare function transformFieldNamesToIds<T extends Record<string, any>>(data: T, baseTable: BaseTable<any, any, any, any>): Record<string, any>;
|
|
10
|
+
/**
|
|
11
|
+
* Transforms FileMaker field IDs (FMFID) to field names in an object
|
|
12
|
+
* @param data - Object with FMFID keys
|
|
13
|
+
* @param baseTable - BaseTable instance to get field names from
|
|
14
|
+
* @returns Object with field names as keys instead of FMFIDs
|
|
15
|
+
*/
|
|
16
|
+
export declare function transformFieldIdsToNames<T extends Record<string, any>>(data: T, baseTable: BaseTable<any, any, any, any>): Record<string, any>;
|
|
17
|
+
/**
|
|
18
|
+
* Transforms a field name to FMFID or returns the field name if not using IDs
|
|
19
|
+
* @param fieldName - The field name to transform
|
|
20
|
+
* @param baseTable - BaseTable instance to get field ID from
|
|
21
|
+
* @returns The FMFID or field name
|
|
22
|
+
*/
|
|
23
|
+
export declare function transformFieldName(fieldName: string, baseTable: BaseTable<any, any, any, any>): string;
|
|
24
|
+
/**
|
|
25
|
+
* Transforms a table occurrence name to FMTID or returns the name if not using IDs
|
|
26
|
+
* @param occurrence - TableOccurrence instance to get table ID from
|
|
27
|
+
* @returns The FMTID or table name
|
|
28
|
+
*/
|
|
29
|
+
export declare function transformTableName(occurrence: TableOccurrence<any, any, any, any>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Transforms response data by converting field IDs back to field names recursively.
|
|
32
|
+
* Handles both single records and arrays of records, as well as nested expand relationships.
|
|
33
|
+
*
|
|
34
|
+
* @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)
|
|
35
|
+
* @param baseTable - BaseTable instance for the main table
|
|
36
|
+
* @param expandConfigs - Configuration for expanded relations (optional)
|
|
37
|
+
* @returns Transformed data with field names instead of IDs
|
|
38
|
+
*/
|
|
39
|
+
export declare function transformResponseFields(data: any, baseTable: BaseTable<any, any, any, any>, expandConfigs?: Array<{
|
|
40
|
+
relation: string;
|
|
41
|
+
occurrence?: TableOccurrence<any, any, any, any>;
|
|
42
|
+
}>): any;
|
|
43
|
+
/**
|
|
44
|
+
* Transforms an array of field names to FMFIDs
|
|
45
|
+
* @param fieldNames - Array of field names
|
|
46
|
+
* @param baseTable - BaseTable instance to get field IDs from
|
|
47
|
+
* @returns Array of FMFIDs or field names
|
|
48
|
+
*/
|
|
49
|
+
export declare function transformFieldNamesArray(fieldNames: string[], baseTable: BaseTable<any, any, any, any>): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Transforms a field name in an orderBy string (e.g., "name desc" -> "FMFID:1 desc")
|
|
52
|
+
* @param orderByString - The orderBy string (field name with optional asc/desc)
|
|
53
|
+
* @param baseTable - BaseTable instance to get field ID from
|
|
54
|
+
* @returns Transformed orderBy string with FMFID
|
|
55
|
+
*/
|
|
56
|
+
export declare function transformOrderByField(orderByString: string, baseTable: BaseTable<any, any, any, any>): string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
function transformFieldNamesToIds(data, baseTable) {
|
|
2
|
+
if (!baseTable.isUsingFieldIds()) {
|
|
3
|
+
return data;
|
|
4
|
+
}
|
|
5
|
+
const transformed = {};
|
|
6
|
+
for (const [fieldName, value] of Object.entries(data)) {
|
|
7
|
+
const fieldId = baseTable.getFieldId(fieldName);
|
|
8
|
+
transformed[fieldId] = value;
|
|
9
|
+
}
|
|
10
|
+
return transformed;
|
|
11
|
+
}
|
|
12
|
+
function transformFieldName(fieldName, baseTable) {
|
|
13
|
+
return baseTable.getFieldId(fieldName);
|
|
14
|
+
}
|
|
15
|
+
function transformTableName(occurrence) {
|
|
16
|
+
return occurrence.getTableId();
|
|
17
|
+
}
|
|
18
|
+
function transformResponseFields(data, baseTable, expandConfigs) {
|
|
19
|
+
if (!baseTable.isUsingFieldIds()) {
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
if (data === null || data === void 0) {
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
if (data.value && Array.isArray(data.value)) {
|
|
26
|
+
return {
|
|
27
|
+
...data,
|
|
28
|
+
value: data.value.map(
|
|
29
|
+
(record) => transformSingleRecord(record, baseTable, expandConfigs)
|
|
30
|
+
)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(data)) {
|
|
34
|
+
return data.map(
|
|
35
|
+
(record) => transformSingleRecord(record, baseTable, expandConfigs)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return transformSingleRecord(data, baseTable, expandConfigs);
|
|
39
|
+
}
|
|
40
|
+
function transformSingleRecord(record, baseTable, expandConfigs) {
|
|
41
|
+
if (!record || typeof record !== "object") {
|
|
42
|
+
return record;
|
|
43
|
+
}
|
|
44
|
+
const transformed = {};
|
|
45
|
+
for (const [key, value] of Object.entries(record)) {
|
|
46
|
+
if (key.startsWith("@")) {
|
|
47
|
+
transformed[key] = value;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
let expandConfig = expandConfigs == null ? void 0 : expandConfigs.find((ec) => ec.relation === key);
|
|
51
|
+
if (!expandConfig && key.startsWith("FMTID:")) {
|
|
52
|
+
expandConfig = expandConfigs == null ? void 0 : expandConfigs.find(
|
|
53
|
+
(ec) => ec.occurrence && ec.occurrence.isUsingTableId() && ec.occurrence.getTableId() === key
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (expandConfig && expandConfig.occurrence) {
|
|
57
|
+
const relationKey = expandConfig.relation;
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
transformed[relationKey] = value.map(
|
|
60
|
+
(nestedRecord) => transformSingleRecord(
|
|
61
|
+
nestedRecord,
|
|
62
|
+
expandConfig.occurrence.baseTable,
|
|
63
|
+
void 0
|
|
64
|
+
// Don't pass nested expand configs for now
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
} else if (value && typeof value === "object") {
|
|
68
|
+
transformed[relationKey] = transformSingleRecord(
|
|
69
|
+
value,
|
|
70
|
+
expandConfig.occurrence.baseTable,
|
|
71
|
+
void 0
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
transformed[relationKey] = value;
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const fieldName = baseTable.getFieldName(key);
|
|
79
|
+
transformed[fieldName] = value;
|
|
80
|
+
}
|
|
81
|
+
return transformed;
|
|
82
|
+
}
|
|
83
|
+
function transformFieldNamesArray(fieldNames, baseTable) {
|
|
84
|
+
if (!baseTable.isUsingFieldIds()) {
|
|
85
|
+
return fieldNames;
|
|
86
|
+
}
|
|
87
|
+
return fieldNames.map((fieldName) => baseTable.getFieldId(fieldName));
|
|
88
|
+
}
|
|
89
|
+
function transformOrderByField(orderByString, baseTable) {
|
|
90
|
+
if (!baseTable.isUsingFieldIds()) {
|
|
91
|
+
return orderByString;
|
|
92
|
+
}
|
|
93
|
+
const parts = orderByString.trim().split(/\s+/);
|
|
94
|
+
const fieldName = parts[0];
|
|
95
|
+
const direction = parts[1];
|
|
96
|
+
const fieldId = baseTable.getFieldId(fieldName);
|
|
97
|
+
return direction ? `${fieldId} ${direction}` : fieldId;
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
transformFieldName,
|
|
101
|
+
transformFieldNamesArray,
|
|
102
|
+
transformFieldNamesToIds,
|
|
103
|
+
transformOrderByField,
|
|
104
|
+
transformResponseFields,
|
|
105
|
+
transformTableName
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sources":["../../src/transform.ts"],"sourcesContent":["import type { BaseTable } from \"./client/base-table\";\nimport type { TableOccurrence } from \"./client/table-occurrence\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Transforms field names to FileMaker field IDs (FMFID) in an object\n * @param data - Object with field names as keys\n * @param baseTable - BaseTable instance to get field IDs from\n * @returns Object with FMFID keys instead of field names\n */\nexport function transformFieldNamesToIds<T extends Record<string, any>>(\n data: T,\n baseTable: BaseTable<any, any, any, any>,\n): Record<string, any> {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [fieldName, value] of Object.entries(data)) {\n const fieldId = baseTable.getFieldId(fieldName as any);\n transformed[fieldId] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms FileMaker field IDs (FMFID) to field names in an object\n * @param data - Object with FMFID keys\n * @param baseTable - BaseTable instance to get field names from\n * @returns Object with field names as keys instead of FMFIDs\n */\nexport function transformFieldIdsToNames<T extends Record<string, any>>(\n data: T,\n baseTable: BaseTable<any, any, any, any>,\n): Record<string, any> {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n const transformed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this is an OData metadata field (starts with @)\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n \n const fieldName = baseTable.getFieldName(key);\n transformed[fieldName] = value;\n }\n return transformed;\n}\n\n/**\n * Transforms a field name to FMFID or returns the field name if not using IDs\n * @param fieldName - The field name to transform\n * @param baseTable - BaseTable instance to get field ID from\n * @returns The FMFID or field name\n */\nexport function transformFieldName(\n fieldName: string,\n baseTable: BaseTable<any, any, any, any>,\n): string {\n return baseTable.getFieldId(fieldName as any);\n}\n\n/**\n * Transforms a table occurrence name to FMTID or returns the name if not using IDs\n * @param occurrence - TableOccurrence instance to get table ID from\n * @returns The FMTID or table name\n */\nexport function transformTableName(\n occurrence: TableOccurrence<any, any, any, any>,\n): string {\n return occurrence.getTableId();\n}\n\n/**\n * Transforms response data by converting field IDs back to field names recursively.\n * Handles both single records and arrays of records, as well as nested expand relationships.\n * \n * @param data - Response data from FileMaker (can be single record, array, or wrapped in value property)\n * @param baseTable - BaseTable instance for the main table\n * @param expandConfigs - Configuration for expanded relations (optional)\n * @returns Transformed data with field names instead of IDs\n */\nexport function transformResponseFields(\n data: any,\n baseTable: BaseTable<any, any, any, any>,\n expandConfigs?: Array<{\n relation: string;\n occurrence?: TableOccurrence<any, any, any, any>;\n }>,\n): any {\n if (!baseTable.isUsingFieldIds()) {\n return data;\n }\n\n // Handle null/undefined\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle OData list response with value array\n if (data.value && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((record: any) =>\n transformSingleRecord(record, baseTable, expandConfigs),\n ),\n };\n }\n\n // Handle array of records\n if (Array.isArray(data)) {\n return data.map((record) =>\n transformSingleRecord(record, baseTable, expandConfigs),\n );\n }\n\n // Handle single record\n return transformSingleRecord(data, baseTable, expandConfigs);\n}\n\n/**\n * Transforms a single record, converting field IDs to names and handling nested expands\n */\nfunction transformSingleRecord(\n record: any,\n baseTable: BaseTable<any, any, any, any>,\n expandConfigs?: Array<{\n relation: string;\n occurrence?: TableOccurrence<any, any, any, any>;\n }>,\n): any {\n if (!record || typeof record !== \"object\") {\n return record;\n }\n\n const transformed: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(record)) {\n // Preserve OData metadata fields\n if (key.startsWith(\"@\")) {\n transformed[key] = value;\n continue;\n }\n\n // Check if this is an expanded relation (by relation name)\n let expandConfig = expandConfigs?.find((ec) => ec.relation === key);\n \n // If not found by relation name, check if this key is a FMTID\n // (FileMaker returns expanded relations with FMTID keys when using entity IDs)\n if (!expandConfig && key.startsWith(\"FMTID:\")) {\n expandConfig = expandConfigs?.find(\n (ec) =>\n ec.occurrence &&\n ec.occurrence.isUsingTableId() &&\n ec.occurrence.getTableId() === key,\n );\n }\n \n if (expandConfig && expandConfig.occurrence) {\n // Transform the expanded relation data recursively\n // Use the relation name (not the FMTID) as the key\n const relationKey = expandConfig.relation;\n \n if (Array.isArray(value)) {\n transformed[relationKey] = value.map((nestedRecord) =>\n transformSingleRecord(\n nestedRecord,\n expandConfig.occurrence!.baseTable,\n undefined, // Don't pass nested expand configs for now\n ),\n );\n } else if (value && typeof value === \"object\") {\n transformed[relationKey] = transformSingleRecord(\n value,\n expandConfig.occurrence.baseTable,\n undefined,\n );\n } else {\n transformed[relationKey] = value;\n }\n continue;\n }\n\n // Transform field ID to field name\n const fieldName = baseTable.getFieldName(key);\n transformed[fieldName] = value;\n }\n\n return transformed;\n}\n\n/**\n * Transforms an array of field names to FMFIDs\n * @param fieldNames - Array of field names\n * @param baseTable - BaseTable instance to get field IDs from\n * @returns Array of FMFIDs or field names\n */\nexport function transformFieldNamesArray(\n fieldNames: string[],\n baseTable: BaseTable<any, any, any, any>,\n): string[] {\n if (!baseTable.isUsingFieldIds()) {\n return fieldNames;\n }\n\n return fieldNames.map((fieldName) => baseTable.getFieldId(fieldName as any));\n}\n\n/**\n * Transforms a field name in an orderBy string (e.g., \"name desc\" -> \"FMFID:1 desc\")\n * @param orderByString - The orderBy string (field name with optional asc/desc)\n * @param baseTable - BaseTable instance to get field ID from\n * @returns Transformed orderBy string with FMFID\n */\nexport function transformOrderByField(\n orderByString: string,\n baseTable: BaseTable<any, any, any, any>,\n): string {\n if (!baseTable.isUsingFieldIds()) {\n return orderByString;\n }\n\n // Parse the orderBy string to extract field name and direction\n const parts = orderByString.trim().split(/\\s+/);\n const fieldName = parts[0];\n const direction = parts[1]; // \"asc\" or \"desc\" or undefined\n\n const fieldId = baseTable.getFieldId(fieldName as any);\n return direction ? `${fieldId} ${direction}` : fieldId;\n}\n\n"],"names":[],"mappings":"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"}
|