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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +489 -334
  2. package/dist/esm/client/batch-builder.d.ts +7 -4
  3. package/dist/esm/client/batch-builder.js +84 -25
  4. package/dist/esm/client/batch-builder.js.map +1 -1
  5. package/dist/esm/client/builders/default-select.d.ts +7 -0
  6. package/dist/esm/client/builders/default-select.js +42 -0
  7. package/dist/esm/client/builders/default-select.js.map +1 -0
  8. package/dist/esm/client/builders/expand-builder.d.ts +43 -0
  9. package/dist/esm/client/builders/expand-builder.js +173 -0
  10. package/dist/esm/client/builders/expand-builder.js.map +1 -0
  11. package/dist/esm/client/builders/index.d.ts +8 -0
  12. package/dist/esm/client/builders/query-string-builder.d.ts +15 -0
  13. package/dist/esm/client/builders/query-string-builder.js +25 -0
  14. package/dist/esm/client/builders/query-string-builder.js.map +1 -0
  15. package/dist/esm/client/builders/response-processor.d.ts +39 -0
  16. package/dist/esm/client/builders/response-processor.js +170 -0
  17. package/dist/esm/client/builders/response-processor.js.map +1 -0
  18. package/dist/esm/client/builders/select-mixin.d.ts +31 -0
  19. package/dist/esm/client/builders/select-mixin.js +30 -0
  20. package/dist/esm/client/builders/select-mixin.js.map +1 -0
  21. package/dist/esm/client/builders/select-utils.d.ts +8 -0
  22. package/dist/esm/client/builders/select-utils.js +15 -0
  23. package/dist/esm/client/builders/select-utils.js.map +1 -0
  24. package/dist/esm/client/builders/shared-types.d.ts +39 -0
  25. package/dist/esm/client/builders/table-utils.d.ts +35 -0
  26. package/dist/esm/client/builders/table-utils.js +45 -0
  27. package/dist/esm/client/builders/table-utils.js.map +1 -0
  28. package/dist/esm/client/database.d.ts +3 -22
  29. package/dist/esm/client/database.js +14 -76
  30. package/dist/esm/client/database.js.map +1 -1
  31. package/dist/esm/client/delete-builder.d.ts +11 -15
  32. package/dist/esm/client/delete-builder.js +26 -26
  33. package/dist/esm/client/delete-builder.js.map +1 -1
  34. package/dist/esm/client/entity-set.d.ts +32 -32
  35. package/dist/esm/client/entity-set.js +92 -69
  36. package/dist/esm/client/entity-set.js.map +1 -1
  37. package/dist/esm/client/error-parser.d.ts +12 -0
  38. package/dist/esm/client/error-parser.js +30 -0
  39. package/dist/esm/client/error-parser.js.map +1 -0
  40. package/dist/esm/client/filemaker-odata.d.ts +2 -4
  41. package/dist/esm/client/filemaker-odata.js +1 -5
  42. package/dist/esm/client/filemaker-odata.js.map +1 -1
  43. package/dist/esm/client/insert-builder.d.ts +7 -9
  44. package/dist/esm/client/insert-builder.js +70 -24
  45. package/dist/esm/client/insert-builder.js.map +1 -1
  46. package/dist/esm/client/query/expand-builder.d.ts +35 -0
  47. package/dist/esm/client/query/index.d.ts +3 -0
  48. package/dist/esm/client/query/query-builder.d.ts +134 -0
  49. package/dist/esm/client/query/query-builder.js +505 -0
  50. package/dist/esm/client/query/query-builder.js.map +1 -0
  51. package/dist/esm/client/query/response-processor.d.ts +22 -0
  52. package/dist/esm/client/query/types.d.ts +52 -0
  53. package/dist/esm/client/query/url-builder.d.ts +71 -0
  54. package/dist/esm/client/query/url-builder.js +107 -0
  55. package/dist/esm/client/query/url-builder.js.map +1 -0
  56. package/dist/esm/client/query-builder.d.ts +1 -111
  57. package/dist/esm/client/record-builder.d.ts +56 -63
  58. package/dist/esm/client/record-builder.js +158 -296
  59. package/dist/esm/client/record-builder.js.map +1 -1
  60. package/dist/esm/client/response-processor.d.ts +3 -3
  61. package/dist/esm/client/update-builder.d.ts +16 -21
  62. package/dist/esm/client/update-builder.js +56 -30
  63. package/dist/esm/client/update-builder.js.map +1 -1
  64. package/dist/esm/errors.d.ts +8 -1
  65. package/dist/esm/errors.js +17 -0
  66. package/dist/esm/errors.js.map +1 -1
  67. package/dist/esm/index.d.ts +3 -7
  68. package/dist/esm/index.js +37 -8
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/orm/column.d.ts +45 -0
  71. package/dist/esm/orm/column.js +59 -0
  72. package/dist/esm/orm/column.js.map +1 -0
  73. package/dist/esm/orm/field-builders.d.ts +154 -0
  74. package/dist/esm/orm/field-builders.js +152 -0
  75. package/dist/esm/orm/field-builders.js.map +1 -0
  76. package/dist/esm/orm/index.d.ts +4 -0
  77. package/dist/esm/orm/operators.d.ts +175 -0
  78. package/dist/esm/orm/operators.js +221 -0
  79. package/dist/esm/orm/operators.js.map +1 -0
  80. package/dist/esm/orm/table.d.ts +341 -0
  81. package/dist/esm/orm/table.js +211 -0
  82. package/dist/esm/orm/table.js.map +1 -0
  83. package/dist/esm/transform.d.ts +20 -21
  84. package/dist/esm/transform.js +34 -34
  85. package/dist/esm/transform.js.map +1 -1
  86. package/dist/esm/types.d.ts +16 -13
  87. package/dist/esm/types.js.map +1 -1
  88. package/dist/esm/validation.d.ts +14 -4
  89. package/dist/esm/validation.js +45 -1
  90. package/dist/esm/validation.js.map +1 -1
  91. package/package.json +5 -2
  92. package/src/client/batch-builder.ts +100 -32
  93. package/src/client/builders/default-select.ts +69 -0
  94. package/src/client/builders/expand-builder.ts +236 -0
  95. package/src/client/builders/index.ts +11 -0
  96. package/src/client/builders/query-string-builder.ts +41 -0
  97. package/src/client/builders/response-processor.ts +273 -0
  98. package/src/client/builders/select-mixin.ts +74 -0
  99. package/src/client/builders/select-utils.ts +34 -0
  100. package/src/client/builders/shared-types.ts +41 -0
  101. package/src/client/builders/table-utils.ts +87 -0
  102. package/src/client/database.ts +19 -160
  103. package/src/client/delete-builder.ts +46 -51
  104. package/src/client/entity-set.ts +227 -302
  105. package/src/client/error-parser.ts +59 -0
  106. package/src/client/filemaker-odata.ts +3 -14
  107. package/src/client/insert-builder.ts +124 -43
  108. package/src/client/query/expand-builder.ts +164 -0
  109. package/src/client/query/index.ts +13 -0
  110. package/src/client/query/query-builder.ts +816 -0
  111. package/src/client/query/response-processor.ts +244 -0
  112. package/src/client/query/types.ts +102 -0
  113. package/src/client/query/url-builder.ts +179 -0
  114. package/src/client/query-builder.ts +8 -1447
  115. package/src/client/record-builder.ts +325 -583
  116. package/src/client/response-processor.ts +4 -5
  117. package/src/client/update-builder.ts +102 -73
  118. package/src/errors.ts +22 -1
  119. package/src/index.ts +55 -5
  120. package/src/orm/column.ts +78 -0
  121. package/src/orm/field-builders.ts +296 -0
  122. package/src/orm/index.ts +60 -0
  123. package/src/orm/operators.ts +428 -0
  124. package/src/orm/table.ts +759 -0
  125. package/src/transform.ts +62 -48
  126. package/src/types.ts +20 -63
  127. package/src/validation.ts +76 -4
  128. package/dist/esm/client/base-table.d.ts +0 -128
  129. package/dist/esm/client/base-table.js +0 -57
  130. package/dist/esm/client/base-table.js.map +0 -1
  131. package/dist/esm/client/build-occurrences.d.ts +0 -74
  132. package/dist/esm/client/build-occurrences.js +0 -31
  133. package/dist/esm/client/build-occurrences.js.map +0 -1
  134. package/dist/esm/client/query-builder.js +0 -897
  135. package/dist/esm/client/query-builder.js.map +0 -1
  136. package/dist/esm/client/table-occurrence.d.ts +0 -86
  137. package/dist/esm/client/table-occurrence.js +0 -58
  138. package/dist/esm/client/table-occurrence.js.map +0 -1
  139. package/src/client/base-table.ts +0 -178
  140. package/src/client/build-occurrences.ts +0 -155
  141. package/src/client/query-builder.ts.bak +0 -1457
  142. package/src/client/table-occurrence.ts +0 -156
@@ -1,16 +1,18 @@
1
1
  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
- import { getAcceptHeader } from "../types.js";
5
- import { getTableIdentifiers, transformTableName, transformFieldNamesArray, transformResponseFields } from "../transform.js";
4
+ import { getTableName, getNavigationPaths } from "../orm/table.js";
6
5
  import { safeJsonParse } from "./sanitize-json.js";
7
- import { QueryBuilder } from "./query-builder.js";
8
- import { validateSingleResponse } from "../validation.js";
9
- import buildQuery from "odata-query";
6
+ import { parseErrorResponse } from "./error-parser.js";
7
+ import { QueryBuilder } from "./query/query-builder.js";
8
+ import { mergeExecuteOptions, resolveTableId, createODataRequest } from "./builders/table-utils.js";
9
+ import { processSelectWithRenames } from "./builders/select-mixin.js";
10
+ import { ExpandBuilder } from "./builders/expand-builder.js";
11
+ import { processODataResponse, getSchemaFromTable } from "./builders/response-processor.js";
12
+ import { buildSelectExpandQueryString } from "./builders/query-string-builder.js";
10
13
  class RecordBuilder {
11
14
  constructor(config) {
12
- __publicField(this, "occurrence");
13
- __publicField(this, "tableName");
15
+ __publicField(this, "table");
14
16
  __publicField(this, "databaseName");
15
17
  __publicField(this, "context");
16
18
  __publicField(this, "recordId");
@@ -20,11 +22,12 @@ class RecordBuilder {
20
22
  __publicField(this, "navigateRelation");
21
23
  __publicField(this, "navigateSourceTableName");
22
24
  __publicField(this, "databaseUseEntityIds");
23
- // New properties for select/expand support
25
+ // Properties for select/expand support
24
26
  __publicField(this, "selectedFields");
25
27
  __publicField(this, "expandConfigs", []);
26
- this.occurrence = config.occurrence;
27
- this.tableName = config.tableName;
28
+ // Mapping from field names to output keys (for renamed fields in select)
29
+ __publicField(this, "fieldMapping");
30
+ this.table = config.occurrence;
28
31
  this.databaseName = config.databaseName;
29
32
  this.context = config.context;
30
33
  this.recordId = config.recordId;
@@ -34,306 +37,180 @@ class RecordBuilder {
34
37
  * Helper to merge database-level useEntityIds with per-request options
35
38
  */
36
39
  mergeExecuteOptions(options) {
37
- return {
38
- ...options,
39
- useEntityIds: (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds
40
- };
40
+ return mergeExecuteOptions(options, this.databaseUseEntityIds);
41
41
  }
42
42
  /**
43
43
  * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
44
44
  * @param useEntityIds - Optional override for entity ID usage
45
45
  */
46
46
  getTableId(useEntityIds) {
47
- var _a, _b;
48
- if (!this.occurrence) {
49
- return this.tableName;
50
- }
51
- const contextDefault = ((_b = (_a = this.context)._getUseEntityIds) == null ? void 0 : _b.call(_a)) ?? false;
52
- const shouldUseIds = useEntityIds ?? contextDefault;
53
- if (shouldUseIds) {
54
- const identifiers = getTableIdentifiers(this.occurrence);
55
- if (!identifiers.id) {
56
- throw new Error(
57
- `useEntityIds is true but TableOccurrence "${identifiers.name}" does not have an fmtId defined`
58
- );
59
- }
60
- return identifiers.id;
47
+ if (!this.table) {
48
+ throw new Error("Table occurrence is required");
61
49
  }
62
- return this.occurrence.getTableName();
50
+ return resolveTableId(
51
+ this.table,
52
+ getTableName(this.table),
53
+ this.context,
54
+ useEntityIds
55
+ );
63
56
  }
64
- getSingleField(field) {
57
+ /**
58
+ * Creates a new RecordBuilder with modified configuration.
59
+ * Used by select() to create new instances.
60
+ */
61
+ cloneWithChanges(changes) {
65
62
  const newBuilder = new RecordBuilder({
66
- occurrence: this.occurrence,
67
- tableName: this.tableName,
63
+ occurrence: this.table,
68
64
  databaseName: this.databaseName,
69
65
  context: this.context,
70
66
  recordId: this.recordId,
71
67
  databaseUseEntityIds: this.databaseUseEntityIds
72
68
  });
73
- newBuilder.operation = "getSingleField";
74
- newBuilder.operationParam = field.toString();
69
+ newBuilder.selectedFields = changes.selectedFields ?? this.selectedFields;
70
+ newBuilder.fieldMapping = changes.fieldMapping ?? this.fieldMapping;
71
+ newBuilder.expandConfigs = [...this.expandConfigs];
75
72
  newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
76
73
  newBuilder.navigateRelation = this.navigateRelation;
77
74
  newBuilder.navigateSourceTableName = this.navigateSourceTableName;
78
75
  return newBuilder;
79
76
  }
80
- /**
81
- * Select specific fields to retrieve from the record.
82
- * Only the selected fields will be returned in the response.
83
- *
84
- * @example
85
- * ```typescript
86
- * const contact = await db.from("contacts").get("uuid").select("name", "email").execute();
87
- * // contact.data has type { name: string; email: string }
88
- * ```
89
- */
90
- select(...fields) {
91
- const uniqueFields = [...new Set(fields)];
77
+ getSingleField(field) {
92
78
  const newBuilder = new RecordBuilder({
93
- occurrence: this.occurrence,
94
- tableName: this.tableName,
79
+ occurrence: this.table,
95
80
  databaseName: this.databaseName,
96
81
  context: this.context,
97
82
  recordId: this.recordId,
98
83
  databaseUseEntityIds: this.databaseUseEntityIds
99
84
  });
100
- newBuilder.selectedFields = uniqueFields.map((f) => String(f));
101
- newBuilder.expandConfigs = [...this.expandConfigs];
85
+ newBuilder.operation = "getSingleField";
86
+ newBuilder.operationParam = field.toString();
102
87
  newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
103
88
  newBuilder.navigateRelation = this.navigateRelation;
104
89
  newBuilder.navigateSourceTableName = this.navigateSourceTableName;
105
90
  return newBuilder;
106
91
  }
92
+ /**
93
+ * Select fields using column references.
94
+ * Allows renaming fields by using different keys in the object.
95
+ * Container fields cannot be selected and will cause a type error.
96
+ *
97
+ * @example
98
+ * db.from(contacts).get("uuid").select({
99
+ * name: contacts.name,
100
+ * userEmail: contacts.email // renamed!
101
+ * })
102
+ *
103
+ * @param fields - Object mapping output keys to column references (container fields excluded)
104
+ * @returns RecordBuilder with updated selected fields
105
+ */
106
+ select(fields) {
107
+ const tableName = getTableName(this.table);
108
+ const { selectedFields, fieldMapping } = processSelectWithRenames(
109
+ fields,
110
+ tableName
111
+ );
112
+ return this.cloneWithChanges({
113
+ selectedFields,
114
+ fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : void 0
115
+ });
116
+ }
107
117
  /**
108
118
  * Expand a navigation property to include related records.
109
119
  * Supports nested select, filter, orderBy, and expand operations.
110
120
  *
111
121
  * @example
112
122
  * ```typescript
113
- * // Simple expand
114
- * const contact = await db.from("contacts").get("uuid").expand("users").execute();
123
+ * // Simple expand with FMTable object
124
+ * const contact = await db.from(contacts).get("uuid").expand(users).execute();
115
125
  *
116
126
  * // Expand with select
117
- * const contact = await db.from("contacts").get("uuid")
118
- * .expand("users", b => b.select("username", "email"))
127
+ * const contact = await db.from(contacts).get("uuid")
128
+ * .expand(users, b => b.select({ username: users.username, email: users.email }))
119
129
  * .execute();
120
130
  * ```
121
131
  */
122
- expand(relation, callback) {
123
- var _a, _b;
124
- const targetOccurrence = (_a = this.occurrence) == null ? void 0 : _a.navigation[relation];
125
- const getDefaultSelectFields = () => {
126
- var _a2;
127
- if (!targetOccurrence) return void 0;
128
- const defaultSelect = targetOccurrence.defaultSelect;
129
- if (defaultSelect === "schema") {
130
- const schema = (_a2 = targetOccurrence.baseTable) == null ? void 0 : _a2.schema;
131
- if (schema) {
132
- return [...new Set(Object.keys(schema))];
133
- }
134
- } else if (Array.isArray(defaultSelect)) {
135
- return [...new Set(defaultSelect)];
136
- }
137
- return void 0;
138
- };
132
+ expand(targetTable, callback) {
139
133
  const newBuilder = new RecordBuilder({
140
- occurrence: this.occurrence,
141
- tableName: this.tableName,
134
+ occurrence: this.table,
142
135
  databaseName: this.databaseName,
143
136
  context: this.context,
144
137
  recordId: this.recordId,
145
138
  databaseUseEntityIds: this.databaseUseEntityIds
146
139
  });
147
140
  newBuilder.selectedFields = this.selectedFields;
141
+ newBuilder.fieldMapping = this.fieldMapping;
148
142
  newBuilder.expandConfigs = [...this.expandConfigs];
149
143
  newBuilder.isNavigateFromEntitySet = this.isNavigateFromEntitySet;
150
144
  newBuilder.navigateRelation = this.navigateRelation;
151
145
  newBuilder.navigateSourceTableName = this.navigateSourceTableName;
152
- if (callback) {
153
- const targetBuilder = new QueryBuilder({
154
- occurrence: targetOccurrence,
155
- tableName: (targetOccurrence == null ? void 0 : targetOccurrence.name) ?? relation,
146
+ const expandBuilder = new ExpandBuilder(this.databaseUseEntityIds);
147
+ const expandConfig = expandBuilder.processExpand(
148
+ targetTable,
149
+ this.table ?? void 0,
150
+ callback,
151
+ () => new QueryBuilder({
152
+ occurrence: targetTable,
156
153
  databaseName: this.databaseName,
157
154
  context: this.context,
158
155
  databaseUseEntityIds: this.databaseUseEntityIds
159
- });
160
- const typedBuilder = targetBuilder;
161
- const configuredBuilder = callback(typedBuilder);
162
- const expandOptions = {
163
- ...configuredBuilder.queryOptions
164
- };
165
- if (!expandOptions.select) {
166
- const defaultFields = getDefaultSelectFields();
167
- if (defaultFields) {
168
- expandOptions.select = defaultFields;
169
- }
170
- }
171
- if (((_b = configuredBuilder.expandConfigs) == null ? void 0 : _b.length) > 0) {
172
- const nestedExpandString = this.buildExpandString(
173
- configuredBuilder.expandConfigs
156
+ })
157
+ );
158
+ newBuilder.expandConfigs.push(expandConfig);
159
+ return newBuilder;
160
+ }
161
+ navigate(targetTable) {
162
+ const relationName = getTableName(targetTable);
163
+ if (this.table) {
164
+ const navigationPaths = getNavigationPaths(this.table);
165
+ if (navigationPaths && !navigationPaths.includes(relationName)) {
166
+ console.warn(
167
+ `Cannot navigate to "${relationName}". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(", ") : "none"}`
174
168
  );
175
- if (nestedExpandString) {
176
- expandOptions.expand = nestedExpandString;
177
- }
178
- }
179
- const expandConfig = {
180
- relation,
181
- options: expandOptions
182
- };
183
- newBuilder.expandConfigs.push(expandConfig);
184
- } else {
185
- const defaultFields = getDefaultSelectFields();
186
- if (defaultFields) {
187
- newBuilder.expandConfigs.push({
188
- relation,
189
- options: { select: defaultFields }
190
- });
191
- } else {
192
- newBuilder.expandConfigs.push({ relation });
193
169
  }
194
170
  }
195
- return newBuilder;
196
- }
197
- // Implementation
198
- navigate(relationName) {
199
- var _a;
200
- const targetOccurrence = (_a = this.occurrence) == null ? void 0 : _a.navigation[relationName];
201
171
  const builder = new QueryBuilder({
202
- occurrence: targetOccurrence,
203
- tableName: (targetOccurrence == null ? void 0 : targetOccurrence.name) ?? relationName,
172
+ occurrence: targetTable,
204
173
  databaseName: this.databaseName,
205
- context: this.context
174
+ context: this.context,
175
+ databaseUseEntityIds: this.databaseUseEntityIds
206
176
  });
207
- const relationId = targetOccurrence ? transformTableName(targetOccurrence) : relationName;
208
- builder.isNavigate = true;
209
- builder.navigateRecordId = this.recordId;
210
- builder.navigateRelation = relationId;
177
+ const relationId = relationName;
178
+ let sourceTableName;
179
+ let baseRelation;
211
180
  if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {
212
- builder.navigateSourceTableName = this.navigateSourceTableName;
213
- builder.navigateBaseRelation = this.navigateRelation;
181
+ sourceTableName = this.navigateSourceTableName;
182
+ baseRelation = this.navigateRelation;
214
183
  } else {
215
- const sourceTableId = this.occurrence ? transformTableName(this.occurrence) : this.tableName;
216
- builder.navigateSourceTableName = sourceTableId;
184
+ if (!this.table) {
185
+ throw new Error("Table occurrence is required for navigation");
186
+ }
187
+ sourceTableName = resolveTableId(
188
+ this.table,
189
+ getTableName(this.table),
190
+ this.context,
191
+ this.databaseUseEntityIds
192
+ );
217
193
  }
194
+ builder.navigation = {
195
+ recordId: this.recordId,
196
+ relation: relationId,
197
+ sourceTableName,
198
+ baseRelation
199
+ };
218
200
  return builder;
219
201
  }
220
- /**
221
- * Formats select fields for use in query strings.
222
- * - Transforms field names to FMFIDs if using entity IDs
223
- * - Wraps "id" fields in double quotes
224
- * - URL-encodes special characters but preserves spaces
225
- */
226
- formatSelectFields(select, baseTable) {
227
- if (!select || select.length === 0) return "";
228
- const transformedFields = baseTable ? transformFieldNamesArray(select, baseTable) : select;
229
- return transformedFields.map((field) => {
230
- if (field === "id") return `"id"`;
231
- const encodedField = encodeURIComponent(String(field));
232
- return encodedField.replace(/%20/g, " ");
233
- }).join(",");
234
- }
235
- /**
236
- * Builds expand validation configs from internal expand configurations.
237
- * These are used to validate expanded navigation properties.
238
- */
239
- buildExpandValidationConfigs(configs) {
240
- return configs.map((config) => {
241
- var _a, _b, _c;
242
- const targetOccurrence = (_a = this.occurrence) == null ? void 0 : _a.navigation[config.relation];
243
- const targetSchema = (_b = targetOccurrence == null ? void 0 : targetOccurrence.baseTable) == null ? void 0 : _b.schema;
244
- const selectedFields = ((_c = config.options) == null ? void 0 : _c.select) ? Array.isArray(config.options.select) ? config.options.select.map((f) => String(f)) : [String(config.options.select)] : void 0;
245
- return {
246
- relation: config.relation,
247
- targetSchema,
248
- targetOccurrence,
249
- targetBaseTable: targetOccurrence == null ? void 0 : targetOccurrence.baseTable,
250
- occurrence: targetOccurrence,
251
- // For transformation
252
- selectedFields,
253
- nestedExpands: void 0
254
- // TODO: Handle nested expands if needed
255
- };
256
- });
257
- }
258
- /**
259
- * Builds OData expand query string from expand configurations.
260
- * Handles nested expands recursively.
261
- * Transforms relation names to FMTIDs if using entity IDs.
262
- */
263
- buildExpandString(configs) {
264
- if (configs.length === 0) {
265
- return "";
266
- }
267
- return configs.map((config) => {
268
- var _a, _b;
269
- const targetOccurrence = (_a = this.occurrence) == null ? void 0 : _a.navigation[config.relation];
270
- const relationName = targetOccurrence && ((_b = targetOccurrence.isUsingTableId) == null ? void 0 : _b.call(targetOccurrence)) ? targetOccurrence.getTableId() : config.relation;
271
- if (!config.options || Object.keys(config.options).length === 0) {
272
- return relationName;
273
- }
274
- const parts = [];
275
- if (config.options.select) {
276
- const selectFields = this.formatSelectFields(
277
- Array.isArray(config.options.select) ? config.options.select.map((f) => String(f)) : [String(config.options.select)],
278
- targetOccurrence == null ? void 0 : targetOccurrence.baseTable
279
- );
280
- parts.push(`$select=${selectFields}`);
281
- }
282
- if (config.options.filter) {
283
- const filterQuery = buildQuery({ filter: config.options.filter });
284
- const filterMatch = filterQuery.match(/\$filter=([^&]+)/);
285
- if (filterMatch) {
286
- parts.push(`$filter=${filterMatch[1]}`);
287
- }
288
- }
289
- if (config.options.orderBy) {
290
- const orderByQuery = buildQuery({ orderBy: config.options.orderBy });
291
- const orderByMatch = orderByQuery.match(/\$orderby=([^&]+)/);
292
- if (orderByMatch) {
293
- parts.push(`$orderby=${orderByMatch[1]}`);
294
- }
295
- }
296
- if (config.options.top !== void 0) {
297
- parts.push(`$top=${config.options.top}`);
298
- }
299
- if (config.options.skip !== void 0) {
300
- parts.push(`$skip=${config.options.skip}`);
301
- }
302
- if (config.options.expand) {
303
- parts.push(`$expand=${String(config.options.expand)}`);
304
- }
305
- if (parts.length === 0) {
306
- return relationName;
307
- }
308
- return `${relationName}(${parts.join(";")})`;
309
- }).join(",");
310
- }
311
202
  /**
312
203
  * Builds the complete query string including $select and $expand parameters.
313
204
  */
314
205
  buildQueryString() {
315
- var _a;
316
- const parts = [];
317
- if (this.selectedFields && this.selectedFields.length > 0) {
318
- const selectString = this.formatSelectFields(
319
- this.selectedFields,
320
- (_a = this.occurrence) == null ? void 0 : _a.baseTable
321
- );
322
- if (selectString) {
323
- parts.push(`$select=${selectString}`);
324
- }
325
- }
326
- const expandString = this.buildExpandString(this.expandConfigs);
327
- if (expandString) {
328
- parts.push(`$expand=${expandString}`);
329
- }
330
- if (parts.length === 0) {
331
- return "";
332
- }
333
- return `?${parts.join("&")}`;
206
+ return buildSelectExpandQueryString({
207
+ selectedFields: this.selectedFields,
208
+ expandConfigs: this.expandConfigs,
209
+ table: this.table,
210
+ useEntityIds: this.databaseUseEntityIds
211
+ });
334
212
  }
335
213
  async execute(options) {
336
- var _a, _b, _c;
337
214
  let url;
338
215
  if (this.isNavigateFromEntitySet && this.navigateSourceTableName && this.navigateRelation) {
339
216
  url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}('${this.recordId}')`;
@@ -359,31 +236,22 @@ class RecordBuilder {
359
236
  const fieldResponse = response;
360
237
  return { data: fieldResponse.value, error: void 0 };
361
238
  }
362
- const shouldUseIds = mergedOptions.useEntityIds ?? false;
363
- const expandValidationConfigs = this.expandConfigs.length > 0 ? this.buildExpandValidationConfigs(this.expandConfigs) : void 0;
364
- if (((_a = this.occurrence) == null ? void 0 : _a.baseTable) && shouldUseIds) {
365
- response = transformResponseFields(
366
- response,
367
- this.occurrence.baseTable,
368
- expandValidationConfigs
369
- );
370
- }
371
- const schema = (_c = (_b = this.occurrence) == null ? void 0 : _b.baseTable) == null ? void 0 : _c.schema;
372
- const validation = await validateSingleResponse(
373
- response,
374
- schema,
375
- this.selectedFields,
376
- expandValidationConfigs,
377
- "exact"
378
- // Expect exactly one record
239
+ const expandBuilder = new ExpandBuilder(
240
+ mergedOptions.useEntityIds ?? false
379
241
  );
380
- if (!validation.valid) {
381
- return { data: void 0, error: validation.error };
382
- }
383
- if (validation.data === null) {
384
- return { data: null, error: void 0 };
385
- }
386
- return { data: validation.data, error: void 0 };
242
+ const expandValidationConfigs = expandBuilder.buildValidationConfigs(
243
+ this.expandConfigs
244
+ );
245
+ return processODataResponse(response, {
246
+ table: this.table,
247
+ schema: getSchemaFromTable(this.table),
248
+ singleMode: "exact",
249
+ selectedFields: this.selectedFields,
250
+ expandValidationConfigs,
251
+ skipValidation: options == null ? void 0 : options.skipValidation,
252
+ useEntityIds: mergedOptions.useEntityIds,
253
+ fieldMapping: this.fieldMapping
254
+ });
387
255
  }
388
256
  getRequestConfig() {
389
257
  let url;
@@ -423,48 +291,42 @@ class RecordBuilder {
423
291
  }
424
292
  toRequest(baseUrl, options) {
425
293
  const config = this.getRequestConfig();
426
- const fullUrl = `${baseUrl}${config.url}`;
427
- return new Request(fullUrl, {
428
- method: config.method,
429
- headers: {
430
- "Content-Type": "application/json",
431
- Accept: getAcceptHeader(options == null ? void 0 : options.includeODataAnnotations)
432
- }
433
- });
294
+ return createODataRequest(baseUrl, config, options);
434
295
  }
435
296
  async processResponse(response, options) {
436
- var _a, _b, _c;
297
+ if (!response.ok) {
298
+ const tableName = this.table ? getTableName(this.table) : "unknown";
299
+ const error = await parseErrorResponse(
300
+ response,
301
+ response.url || `/${this.databaseName}/${tableName}`
302
+ );
303
+ return { data: void 0, error };
304
+ }
437
305
  const rawResponse = await safeJsonParse(response);
438
306
  if (this.operation === "getSingleField") {
439
307
  const fieldResponse = rawResponse;
440
308
  return { data: fieldResponse.value, error: void 0 };
441
309
  }
442
- const shouldUseIds = (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds;
443
- const expandValidationConfigs = this.expandConfigs.length > 0 ? this.buildExpandValidationConfigs(this.expandConfigs) : void 0;
444
- let transformedResponse = rawResponse;
445
- if (((_a = this.occurrence) == null ? void 0 : _a.baseTable) && shouldUseIds) {
446
- transformedResponse = transformResponseFields(
447
- rawResponse,
448
- this.occurrence.baseTable,
449
- expandValidationConfigs
450
- );
451
- }
452
- const schema = (_c = (_b = this.occurrence) == null ? void 0 : _b.baseTable) == null ? void 0 : _c.schema;
453
- const validation = await validateSingleResponse(
454
- transformedResponse,
455
- schema,
456
- this.selectedFields,
457
- expandValidationConfigs,
458
- "exact"
459
- // Expect exactly one record
310
+ const mergedOptions = mergeExecuteOptions(
311
+ options,
312
+ this.databaseUseEntityIds
460
313
  );
461
- if (!validation.valid) {
462
- return { data: void 0, error: validation.error };
463
- }
464
- if (validation.data === null) {
465
- return { data: null, error: void 0 };
466
- }
467
- return { data: validation.data, error: void 0 };
314
+ const expandBuilder = new ExpandBuilder(
315
+ mergedOptions.useEntityIds ?? false
316
+ );
317
+ const expandValidationConfigs = expandBuilder.buildValidationConfigs(
318
+ this.expandConfigs
319
+ );
320
+ return processODataResponse(rawResponse, {
321
+ table: this.table,
322
+ schema: getSchemaFromTable(this.table),
323
+ singleMode: "exact",
324
+ selectedFields: this.selectedFields,
325
+ expandValidationConfigs,
326
+ skipValidation: options == null ? void 0 : options.skipValidation,
327
+ useEntityIds: mergedOptions.useEntityIds,
328
+ fieldMapping: this.fieldMapping
329
+ });
468
330
  }
469
331
  }
470
332
  export {