@proofkit/fmodata 0.1.0-alpha.1 → 0.1.0-alpha.11

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 (79) hide show
  1. package/README.md +760 -69
  2. package/dist/esm/client/base-table.d.ts +120 -5
  3. package/dist/esm/client/base-table.js +43 -5
  4. package/dist/esm/client/base-table.js.map +1 -1
  5. package/dist/esm/client/batch-builder.d.ts +54 -0
  6. package/dist/esm/client/batch-builder.js +179 -0
  7. package/dist/esm/client/batch-builder.js.map +1 -0
  8. package/dist/esm/client/batch-request.d.ts +61 -0
  9. package/dist/esm/client/batch-request.js +252 -0
  10. package/dist/esm/client/batch-request.js.map +1 -0
  11. package/dist/esm/client/build-occurrences.d.ts +74 -0
  12. package/dist/esm/client/build-occurrences.js +31 -0
  13. package/dist/esm/client/build-occurrences.js.map +1 -0
  14. package/dist/esm/client/database.d.ts +55 -6
  15. package/dist/esm/client/database.js +118 -15
  16. package/dist/esm/client/database.js.map +1 -1
  17. package/dist/esm/client/delete-builder.d.ts +21 -2
  18. package/dist/esm/client/delete-builder.js +96 -32
  19. package/dist/esm/client/delete-builder.js.map +1 -1
  20. package/dist/esm/client/entity-set.d.ts +26 -12
  21. package/dist/esm/client/entity-set.js +43 -12
  22. package/dist/esm/client/entity-set.js.map +1 -1
  23. package/dist/esm/client/filemaker-odata.d.ts +23 -4
  24. package/dist/esm/client/filemaker-odata.js +124 -29
  25. package/dist/esm/client/filemaker-odata.js.map +1 -1
  26. package/dist/esm/client/insert-builder.d.ts +38 -3
  27. package/dist/esm/client/insert-builder.js +231 -34
  28. package/dist/esm/client/insert-builder.js.map +1 -1
  29. package/dist/esm/client/query-builder.d.ts +28 -7
  30. package/dist/esm/client/query-builder.js +470 -212
  31. package/dist/esm/client/query-builder.js.map +1 -1
  32. package/dist/esm/client/record-builder.d.ts +96 -10
  33. package/dist/esm/client/record-builder.js +378 -39
  34. package/dist/esm/client/record-builder.js.map +1 -1
  35. package/dist/esm/client/response-processor.d.ts +38 -0
  36. package/dist/esm/client/schema-manager.d.ts +57 -0
  37. package/dist/esm/client/schema-manager.js +132 -0
  38. package/dist/esm/client/schema-manager.js.map +1 -0
  39. package/dist/esm/client/table-occurrence.d.ts +69 -8
  40. package/dist/esm/client/table-occurrence.js +35 -24
  41. package/dist/esm/client/table-occurrence.js.map +1 -1
  42. package/dist/esm/client/update-builder.d.ts +34 -11
  43. package/dist/esm/client/update-builder.js +135 -31
  44. package/dist/esm/client/update-builder.js.map +1 -1
  45. package/dist/esm/errors.d.ts +73 -0
  46. package/dist/esm/errors.js +148 -0
  47. package/dist/esm/errors.js.map +1 -0
  48. package/dist/esm/index.d.ts +13 -3
  49. package/dist/esm/index.js +29 -7
  50. package/dist/esm/index.js.map +1 -1
  51. package/dist/esm/transform.d.ts +65 -0
  52. package/dist/esm/transform.js +114 -0
  53. package/dist/esm/transform.js.map +1 -0
  54. package/dist/esm/types.d.ts +89 -5
  55. package/dist/esm/validation.d.ts +6 -3
  56. package/dist/esm/validation.js +104 -33
  57. package/dist/esm/validation.js.map +1 -1
  58. package/package.json +10 -1
  59. package/src/client/base-table.ts +161 -8
  60. package/src/client/batch-builder.ts +265 -0
  61. package/src/client/batch-request.ts +485 -0
  62. package/src/client/build-occurrences.ts +155 -0
  63. package/src/client/database.ts +175 -18
  64. package/src/client/delete-builder.ts +149 -48
  65. package/src/client/entity-set.ts +134 -28
  66. package/src/client/filemaker-odata.ts +179 -35
  67. package/src/client/insert-builder.ts +350 -40
  68. package/src/client/query-builder.ts +632 -244
  69. package/src/client/query-builder.ts.bak +1457 -0
  70. package/src/client/record-builder.ts +692 -68
  71. package/src/client/response-processor.ts +103 -0
  72. package/src/client/schema-manager.ts +246 -0
  73. package/src/client/table-occurrence.ts +107 -51
  74. package/src/client/update-builder.ts +235 -49
  75. package/src/errors.ts +217 -0
  76. package/src/index.ts +63 -6
  77. package/src/transform.ts +249 -0
  78. package/src/types.ts +201 -35
  79. package/src/validation.ts +120 -36
@@ -3,6 +3,8 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import buildQuery from "odata-query";
5
5
  import { validateSingleResponse, validateListResponse } from "../validation.js";
6
+ import { RecordCountMismatchError } from "../errors.js";
7
+ import { getTableIdentifiers, transformFieldName, transformOrderByField, transformFieldNamesArray, transformResponseFields } from "../transform.js";
6
8
  class QueryBuilder {
7
9
  constructor(config) {
8
10
  __publicField(this, "queryOptions", {});
@@ -18,10 +20,23 @@ class QueryBuilder {
18
20
  __publicField(this, "navigateRelation");
19
21
  __publicField(this, "navigateSourceTableName");
20
22
  __publicField(this, "navigateBaseRelation");
23
+ __publicField(this, "navigateBasePath");
24
+ // Full base path for chained entity set navigations
25
+ __publicField(this, "databaseUseEntityIds");
21
26
  this.occurrence = config.occurrence;
22
27
  this.tableName = config.tableName;
23
28
  this.databaseName = config.databaseName;
24
29
  this.context = config.context;
30
+ this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
31
+ }
32
+ /**
33
+ * Helper to merge database-level useEntityIds with per-request options
34
+ */
35
+ mergeExecuteOptions(options) {
36
+ return {
37
+ ...options,
38
+ useEntityIds: (options == null ? void 0 : options.useEntityIds) === void 0 ? this.databaseUseEntityIds : options.useEntityIds
39
+ };
25
40
  }
26
41
  /**
27
42
  * Helper to conditionally strip OData annotations based on options
@@ -33,13 +48,36 @@ class QueryBuilder {
33
48
  const { "@id": _id, "@editLink": _editLink, ...rest } = data;
34
49
  return rest;
35
50
  }
51
+ /**
52
+ * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
53
+ * @param useEntityIds - Optional override for entity ID usage
54
+ */
55
+ getTableId(useEntityIds) {
56
+ var _a, _b;
57
+ if (!this.occurrence) {
58
+ return this.tableName;
59
+ }
60
+ const contextDefault = ((_b = (_a = this.context)._getUseEntityIds) == null ? void 0 : _b.call(_a)) ?? false;
61
+ const shouldUseIds = useEntityIds ?? contextDefault;
62
+ if (shouldUseIds) {
63
+ const identifiers = getTableIdentifiers(this.occurrence);
64
+ if (!identifiers.id) {
65
+ throw new Error(
66
+ `useEntityIds is true but TableOccurrence "${identifiers.name}" does not have an fmtId defined`
67
+ );
68
+ }
69
+ return identifiers.id;
70
+ }
71
+ return this.occurrence.getTableName();
72
+ }
36
73
  select(...fields) {
37
74
  const uniqueFields = [...new Set(fields)];
38
75
  const newBuilder = new QueryBuilder({
39
76
  occurrence: this.occurrence,
40
77
  tableName: this.tableName,
41
78
  databaseName: this.databaseName,
42
- context: this.context
79
+ context: this.context,
80
+ databaseUseEntityIds: this.databaseUseEntityIds
43
81
  });
44
82
  newBuilder.queryOptions = {
45
83
  ...this.queryOptions,
@@ -62,6 +100,7 @@ class QueryBuilder {
62
100
  * - Shorthand values are handled by odata-query
63
101
  */
64
102
  transformFilter(filter) {
103
+ var _a;
65
104
  if (typeof filter === "string") {
66
105
  return filter;
67
106
  }
@@ -84,12 +123,13 @@ class QueryBuilder {
84
123
  const result = {};
85
124
  const andConditions = [];
86
125
  for (const [field, value] of Object.entries(filter)) {
126
+ const fieldId = ((_a = this.occurrence) == null ? void 0 : _a.baseTable) ? transformFieldName(field, this.occurrence.baseTable) : field;
87
127
  if (Array.isArray(value)) {
88
128
  if (value.length === 1) {
89
- result[field] = value[0];
129
+ result[fieldId] = value[0];
90
130
  } else {
91
131
  for (const op of value) {
92
- andConditions.push({ [field]: op });
132
+ andConditions.push({ [fieldId]: op });
93
133
  }
94
134
  }
95
135
  } else if (value && typeof value === "object" && !(value instanceof Date) && !Array.isArray(value)) {
@@ -107,12 +147,12 @@ class QueryBuilder {
107
147
  ];
108
148
  const isOperatorObject = operatorKeys.some((key) => key in value);
109
149
  if (isOperatorObject) {
110
- result[field] = value;
150
+ result[fieldId] = value;
111
151
  } else {
112
- result[field] = value;
152
+ result[fieldId] = value;
113
153
  }
114
154
  } else {
115
- result[field] = value;
155
+ result[fieldId] = value;
116
156
  }
117
157
  }
118
158
  if (andConditions.length > 0) {
@@ -129,7 +169,21 @@ class QueryBuilder {
129
169
  return this;
130
170
  }
131
171
  orderBy(orderBy) {
132
- this.queryOptions.orderBy = orderBy;
172
+ var _a;
173
+ if (((_a = this.occurrence) == null ? void 0 : _a.baseTable) && orderBy) {
174
+ if (Array.isArray(orderBy)) {
175
+ this.queryOptions.orderBy = orderBy.map(
176
+ (field) => transformOrderByField(String(field), this.occurrence.baseTable)
177
+ );
178
+ } else {
179
+ this.queryOptions.orderBy = transformOrderByField(
180
+ String(orderBy),
181
+ this.occurrence.baseTable
182
+ );
183
+ }
184
+ } else {
185
+ this.queryOptions.orderBy = orderBy;
186
+ }
133
187
  return this;
134
188
  }
135
189
  top(count) {
@@ -142,13 +196,18 @@ class QueryBuilder {
142
196
  }
143
197
  /**
144
198
  * Formats select fields for use in query strings.
199
+ * - Transforms field names to FMFIDs if using entity IDs
145
200
  * - Wraps "id" fields in double quotes
146
201
  * - URL-encodes special characters but preserves spaces
147
202
  */
148
- formatSelectFields(select) {
203
+ formatSelectFields(select, baseTable) {
149
204
  if (!select) return "";
150
205
  const selectFieldsArray = Array.isArray(select) ? select : [select];
151
- return selectFieldsArray.map((field) => {
206
+ const transformedFields = baseTable ? transformFieldNamesArray(
207
+ selectFieldsArray.map((f) => String(f)),
208
+ baseTable
209
+ ) : selectFieldsArray.map((f) => String(f));
210
+ return transformedFields.map((field) => {
152
211
  if (field === "id") return `"id"`;
153
212
  const encodedField = encodeURIComponent(String(field));
154
213
  return encodedField.replace(/%20/g, " ");
@@ -168,6 +227,9 @@ class QueryBuilder {
168
227
  relation: config.relation,
169
228
  targetSchema,
170
229
  targetOccurrence,
230
+ targetBaseTable: targetOccurrence == null ? void 0 : targetOccurrence.baseTable,
231
+ occurrence: targetOccurrence,
232
+ // Add occurrence for transformation
171
233
  selectedFields,
172
234
  nestedExpands: void 0
173
235
  // TODO: Handle nested expands if needed
@@ -177,18 +239,25 @@ class QueryBuilder {
177
239
  /**
178
240
  * Builds OData expand query string from expand configurations.
179
241
  * Handles nested expands recursively.
242
+ * Transforms relation names to FMTIDs if using entity IDs.
180
243
  */
181
244
  buildExpandString(configs) {
182
245
  if (configs.length === 0) {
183
246
  return "";
184
247
  }
185
248
  return configs.map((config) => {
249
+ var _a;
250
+ const targetOccurrence = (_a = this.occurrence) == null ? void 0 : _a.navigation[config.relation];
251
+ const relationName = targetOccurrence && targetOccurrence.isUsingTableId() ? targetOccurrence.getTableId() : config.relation;
186
252
  if (!config.options || Object.keys(config.options).length === 0) {
187
- return config.relation;
253
+ return relationName;
188
254
  }
189
255
  const parts = [];
190
256
  if (config.options.select) {
191
- const selectFields = this.formatSelectFields(config.options.select);
257
+ const selectFields = this.formatSelectFields(
258
+ config.options.select,
259
+ targetOccurrence == null ? void 0 : targetOccurrence.baseTable
260
+ );
192
261
  parts.push(`$select=${selectFields}`);
193
262
  }
194
263
  if (config.options.filter) {
@@ -214,9 +283,9 @@ class QueryBuilder {
214
283
  }
215
284
  }
216
285
  if (parts.length === 0) {
217
- return config.relation;
286
+ return relationName;
218
287
  }
219
- return `${config.relation}(${parts.join(";")})`;
288
+ return `${relationName}(${parts.join(";")})`;
220
289
  }).join(",");
221
290
  }
222
291
  expand(relation, callback) {
@@ -227,7 +296,8 @@ class QueryBuilder {
227
296
  occurrence: targetOccurrence,
228
297
  tableName: (targetOccurrence == null ? void 0 : targetOccurrence.name) ?? relation,
229
298
  databaseName: this.databaseName,
230
- context: this.context
299
+ context: this.context,
300
+ databaseUseEntityIds: this.databaseUseEntityIds
231
301
  });
232
302
  const typedBuilder = targetBuilder;
233
303
  const configuredBuilder = callback(typedBuilder);
@@ -257,7 +327,8 @@ class QueryBuilder {
257
327
  occurrence: this.occurrence,
258
328
  tableName: this.tableName,
259
329
  databaseName: this.databaseName,
260
- context: this.context
330
+ context: this.context,
331
+ databaseUseEntityIds: this.databaseUseEntityIds
261
332
  });
262
333
  newBuilder.queryOptions = { ...this.queryOptions };
263
334
  newBuilder.expandConfigs = [...this.expandConfigs];
@@ -275,7 +346,8 @@ class QueryBuilder {
275
346
  occurrence: this.occurrence,
276
347
  tableName: this.tableName,
277
348
  databaseName: this.databaseName,
278
- context: this.context
349
+ context: this.context,
350
+ databaseUseEntityIds: this.databaseUseEntityIds
279
351
  });
280
352
  newBuilder.queryOptions = { ...this.queryOptions };
281
353
  newBuilder.expandConfigs = [...this.expandConfigs];
@@ -293,7 +365,8 @@ class QueryBuilder {
293
365
  occurrence: this.occurrence,
294
366
  tableName: this.tableName,
295
367
  databaseName: this.databaseName,
296
- context: this.context
368
+ context: this.context,
369
+ databaseUseEntityIds: this.databaseUseEntityIds
297
370
  });
298
371
  newBuilder.queryOptions = { ...this.queryOptions, count: true };
299
372
  newBuilder.expandConfigs = [...this.expandConfigs];
@@ -307,200 +380,144 @@ class QueryBuilder {
307
380
  return newBuilder;
308
381
  }
309
382
  async execute(options) {
310
- var _a, _b, _c, _d, _e, _f;
311
- try {
312
- const queryOptionsWithoutExpand = { ...this.queryOptions };
313
- delete queryOptionsWithoutExpand.expand;
314
- if (queryOptionsWithoutExpand.select) {
315
- queryOptionsWithoutExpand.select = this.formatSelectFields(
316
- queryOptionsWithoutExpand.select
317
- );
383
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
384
+ const queryOptionsWithoutExpand = { ...this.queryOptions };
385
+ delete queryOptionsWithoutExpand.expand;
386
+ const mergedOptions = this.mergeExecuteOptions(options);
387
+ if (queryOptionsWithoutExpand.select) {
388
+ queryOptionsWithoutExpand.select = this.formatSelectFields(
389
+ queryOptionsWithoutExpand.select,
390
+ (_a = this.occurrence) == null ? void 0 : _a.baseTable
391
+ );
392
+ }
393
+ let queryString = buildQuery(queryOptionsWithoutExpand);
394
+ const expandString = this.buildExpandString(this.expandConfigs);
395
+ if (expandString) {
396
+ const separator = queryString.includes("?") ? "&" : "?";
397
+ queryString = `${queryString}${separator}$expand=${expandString}`;
398
+ }
399
+ if (this.isNavigate && this.navigateRecordId && this.navigateRelation && this.navigateSourceTableName) {
400
+ let url;
401
+ if (this.navigateBaseRelation) {
402
+ url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateBaseRelation}('${this.navigateRecordId}')/${this.navigateRelation}${queryString}`;
403
+ } else {
404
+ url = `/${this.databaseName}/${this.navigateSourceTableName}('${this.navigateRecordId}')/${this.navigateRelation}${queryString}`;
318
405
  }
319
- let queryString = buildQuery(queryOptionsWithoutExpand);
320
- const expandString = this.buildExpandString(this.expandConfigs);
321
- if (expandString) {
322
- const separator = queryString.includes("?") ? "&" : "?";
323
- queryString = `${queryString}${separator}$expand=${expandString}`;
406
+ const result2 = await this.context._makeRequest(url, mergedOptions);
407
+ if (result2.error) {
408
+ return { data: void 0, error: result2.error };
324
409
  }
325
- if (this.isNavigate && this.navigateRecordId && this.navigateRelation && this.navigateSourceTableName) {
326
- let url;
327
- if (this.navigateBaseRelation) {
328
- url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateBaseRelation}('${this.navigateRecordId}')/${this.navigateRelation}${queryString}`;
329
- } else {
330
- url = `/${this.databaseName}/${this.navigateSourceTableName}('${this.navigateRecordId}')/${this.navigateRelation}${queryString}`;
331
- }
332
- const response2 = await this.context._makeRequest(url, options);
333
- if ((options == null ? void 0 : options.skipValidation) === true) {
334
- const resp = response2;
335
- if (this.singleMode !== false) {
336
- const records = resp.value ?? [resp];
337
- const count = Array.isArray(records) ? records.length : 1;
338
- if (count > 1) {
339
- return {
340
- data: void 0,
341
- error: new Error(
342
- `Expected ${this.singleMode === "exact" ? "exactly one" : "at most one"} record, but received ${count}`
343
- )
344
- };
345
- }
346
- if (count === 0) {
347
- if (this.singleMode === "exact") {
348
- return {
349
- data: void 0,
350
- error: new Error(
351
- "Expected exactly one record, but received none"
352
- )
353
- };
354
- }
355
- return { data: null, error: void 0 };
356
- }
357
- const record = Array.isArray(records) ? records[0] : records;
358
- const stripped = this.stripODataAnnotationsIfNeeded(
359
- record,
360
- options
361
- );
362
- return { data: stripped, error: void 0 };
363
- } else {
364
- const records = resp.value ?? [];
365
- const stripped = records.map(
366
- (record) => this.stripODataAnnotationsIfNeeded(record, options)
367
- );
368
- return { data: stripped, error: void 0 };
369
- }
370
- }
371
- const schema2 = (_b = (_a = this.occurrence) == null ? void 0 : _a.baseTable) == null ? void 0 : _b.schema;
372
- const selectedFields2 = this.queryOptions.select;
373
- const expandValidationConfigs2 = this.buildExpandValidationConfigs(
410
+ let response2 = result2.data;
411
+ const shouldUseIds2 = mergedOptions.useEntityIds ?? false;
412
+ if (((_b = this.occurrence) == null ? void 0 : _b.baseTable) && shouldUseIds2) {
413
+ const expandValidationConfigs3 = this.buildExpandValidationConfigs(
374
414
  this.expandConfigs
375
415
  );
416
+ response2 = transformResponseFields(
417
+ response2,
418
+ this.occurrence.baseTable,
419
+ expandValidationConfigs3
420
+ );
421
+ }
422
+ if ((options == null ? void 0 : options.skipValidation) === true) {
423
+ const resp = response2;
376
424
  if (this.singleMode !== false) {
377
- const validation = await validateSingleResponse(
378
- response2,
379
- schema2,
380
- selectedFields2,
381
- expandValidationConfigs2,
382
- this.singleMode
383
- );
384
- if (!validation.valid) {
385
- return { data: void 0, error: validation.error };
386
- }
387
- const stripped = validation.data ? this.stripODataAnnotationsIfNeeded(validation.data, options) : null;
388
- return { data: stripped, error: void 0 };
389
- } else {
390
- const validation = await validateListResponse(
391
- response2,
392
- schema2,
393
- selectedFields2,
394
- expandValidationConfigs2
395
- );
396
- if (!validation.valid) {
397
- return { data: void 0, error: validation.error };
425
+ const records = resp.value ?? [resp];
426
+ const count = Array.isArray(records) ? records.length : 1;
427
+ if (count > 1) {
428
+ return {
429
+ data: void 0,
430
+ error: new RecordCountMismatchError(
431
+ this.singleMode === "exact" ? "one" : "at-most-one",
432
+ count
433
+ )
434
+ };
398
435
  }
399
- const stripped = validation.data.map(
400
- (record) => this.stripODataAnnotationsIfNeeded(record, options)
401
- );
402
- return { data: stripped, error: void 0 };
403
- }
404
- }
405
- if (this.isNavigate && !this.navigateRecordId && this.navigateRelation && this.navigateSourceTableName) {
406
- const response2 = await this.context._makeRequest(
407
- `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}${queryString}`,
408
- options
409
- );
410
- if ((options == null ? void 0 : options.skipValidation) === true) {
411
- const resp = response2;
412
- if (this.singleMode !== false) {
413
- const records = resp.value ?? [resp];
414
- const count = Array.isArray(records) ? records.length : 1;
415
- if (count > 1) {
436
+ if (count === 0) {
437
+ if (this.singleMode === "exact") {
416
438
  return {
417
439
  data: void 0,
418
- error: new Error(
419
- `Expected ${this.singleMode === "exact" ? "exactly one" : "at most one"} record, but received ${count}`
420
- )
440
+ error: new RecordCountMismatchError("one", 0)
421
441
  };
422
442
  }
423
- if (count === 0) {
424
- if (this.singleMode === "exact") {
425
- return {
426
- data: void 0,
427
- error: new Error(
428
- "Expected exactly one record, but received none"
429
- )
430
- };
431
- }
432
- return { data: null, error: void 0 };
433
- }
434
- const record = Array.isArray(records) ? records[0] : records;
435
- const stripped = this.stripODataAnnotationsIfNeeded(
436
- record,
437
- options
438
- );
439
- return { data: stripped, error: void 0 };
440
- } else {
441
- const records = resp.value ?? [];
442
- const stripped = records.map(
443
- (record) => this.stripODataAnnotationsIfNeeded(record, options)
444
- );
445
- return { data: stripped, error: void 0 };
446
- }
447
- }
448
- const schema2 = (_d = (_c = this.occurrence) == null ? void 0 : _c.baseTable) == null ? void 0 : _d.schema;
449
- const selectedFields2 = this.queryOptions.select;
450
- const expandValidationConfigs2 = this.buildExpandValidationConfigs(
451
- this.expandConfigs
452
- );
453
- if (this.singleMode !== false) {
454
- const validation = await validateSingleResponse(
455
- response2,
456
- schema2,
457
- selectedFields2,
458
- expandValidationConfigs2,
459
- this.singleMode
460
- );
461
- if (!validation.valid) {
462
- return { data: void 0, error: validation.error };
443
+ return { data: null, error: void 0 };
463
444
  }
464
- const stripped = validation.data ? this.stripODataAnnotationsIfNeeded(validation.data, options) : null;
445
+ const record = Array.isArray(records) ? records[0] : records;
446
+ const stripped = this.stripODataAnnotationsIfNeeded(record, options);
465
447
  return { data: stripped, error: void 0 };
466
448
  } else {
467
- const validation = await validateListResponse(
468
- response2,
469
- schema2,
470
- selectedFields2,
471
- expandValidationConfigs2
472
- );
473
- if (!validation.valid) {
474
- return { data: void 0, error: validation.error };
475
- }
476
- const stripped = validation.data.map(
449
+ const records = resp.value ?? [];
450
+ const stripped = records.map(
477
451
  (record) => this.stripODataAnnotationsIfNeeded(record, options)
478
452
  );
479
453
  return { data: stripped, error: void 0 };
480
454
  }
481
455
  }
482
- if (this.isCountMode) {
483
- const result = await this.context._makeRequest(
484
- `/${this.databaseName}/${this.tableName}/$count${queryString}`,
485
- options
456
+ const schema2 = (_d = (_c = this.occurrence) == null ? void 0 : _c.baseTable) == null ? void 0 : _d.schema;
457
+ const selectedFields2 = this.queryOptions.select;
458
+ const expandValidationConfigs2 = this.buildExpandValidationConfigs(
459
+ this.expandConfigs
460
+ );
461
+ if (this.singleMode !== false) {
462
+ const validation = await validateSingleResponse(
463
+ response2,
464
+ schema2,
465
+ selectedFields2,
466
+ expandValidationConfigs2,
467
+ this.singleMode
468
+ );
469
+ if (!validation.valid) {
470
+ return { data: void 0, error: validation.error };
471
+ }
472
+ const stripped = validation.data ? this.stripODataAnnotationsIfNeeded(validation.data, options) : null;
473
+ return { data: stripped, error: void 0 };
474
+ } else {
475
+ const validation = await validateListResponse(
476
+ response2,
477
+ schema2,
478
+ selectedFields2,
479
+ expandValidationConfigs2
486
480
  );
487
- const count = typeof result === "string" ? Number(result) : result;
488
- return { data: count, error: void 0 };
481
+ if (!validation.valid) {
482
+ return { data: void 0, error: validation.error };
483
+ }
484
+ const stripped = validation.data.map(
485
+ (record) => this.stripODataAnnotationsIfNeeded(record, options)
486
+ );
487
+ return { data: stripped, error: void 0 };
489
488
  }
490
- const response = await this.context._makeRequest(
491
- `/${this.databaseName}/${this.tableName}${queryString}`,
492
- options
489
+ }
490
+ if (this.isNavigate && !this.navigateRecordId && this.navigateRelation && this.navigateSourceTableName) {
491
+ const result2 = await this.context._makeRequest(
492
+ `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}${queryString}`,
493
+ mergedOptions
493
494
  );
495
+ if (result2.error) {
496
+ return { data: void 0, error: result2.error };
497
+ }
498
+ let response2 = result2.data;
499
+ const shouldUseIds2 = mergedOptions.useEntityIds ?? false;
500
+ if (((_e = this.occurrence) == null ? void 0 : _e.baseTable) && shouldUseIds2) {
501
+ const expandValidationConfigs3 = this.buildExpandValidationConfigs(
502
+ this.expandConfigs
503
+ );
504
+ response2 = transformResponseFields(
505
+ response2,
506
+ this.occurrence.baseTable,
507
+ expandValidationConfigs3
508
+ );
509
+ }
494
510
  if ((options == null ? void 0 : options.skipValidation) === true) {
495
- const resp = response;
511
+ const resp = response2;
496
512
  if (this.singleMode !== false) {
497
513
  const records = resp.value ?? [resp];
498
514
  const count = Array.isArray(records) ? records.length : 1;
499
515
  if (count > 1) {
500
516
  return {
501
517
  data: void 0,
502
- error: new Error(
503
- `Expected ${this.singleMode === "exact" ? "exactly one" : "at most one"} record, but received ${count}`
518
+ error: new RecordCountMismatchError(
519
+ this.singleMode === "exact" ? "one" : "at-most-one",
520
+ count
504
521
  )
505
522
  };
506
523
  }
@@ -508,9 +525,7 @@ class QueryBuilder {
508
525
  if (this.singleMode === "exact") {
509
526
  return {
510
527
  data: void 0,
511
- error: new Error(
512
- "Expected exactly one record, but received none"
513
- )
528
+ error: new RecordCountMismatchError("one", 0)
514
529
  };
515
530
  }
516
531
  return { data: null, error: void 0 };
@@ -526,33 +541,30 @@ class QueryBuilder {
526
541
  return { data: stripped, error: void 0 };
527
542
  }
528
543
  }
529
- const schema = (_f = (_e = this.occurrence) == null ? void 0 : _e.baseTable) == null ? void 0 : _f.schema;
530
- const selectedFields = this.queryOptions.select;
531
- const expandValidationConfigs = this.buildExpandValidationConfigs(
544
+ const schema2 = (_g = (_f = this.occurrence) == null ? void 0 : _f.baseTable) == null ? void 0 : _g.schema;
545
+ const selectedFields2 = this.queryOptions.select;
546
+ const expandValidationConfigs2 = this.buildExpandValidationConfigs(
532
547
  this.expandConfigs
533
548
  );
534
549
  if (this.singleMode !== false) {
535
550
  const validation = await validateSingleResponse(
536
- response,
537
- schema,
538
- selectedFields,
539
- expandValidationConfigs,
551
+ response2,
552
+ schema2,
553
+ selectedFields2,
554
+ expandValidationConfigs2,
540
555
  this.singleMode
541
556
  );
542
557
  if (!validation.valid) {
543
558
  return { data: void 0, error: validation.error };
544
559
  }
545
560
  const stripped = validation.data ? this.stripODataAnnotationsIfNeeded(validation.data, options) : null;
546
- return {
547
- data: stripped,
548
- error: void 0
549
- };
561
+ return { data: stripped, error: void 0 };
550
562
  } else {
551
563
  const validation = await validateListResponse(
552
- response,
553
- schema,
554
- selectedFields,
555
- expandValidationConfigs
564
+ response2,
565
+ schema2,
566
+ selectedFields2,
567
+ expandValidationConfigs2
556
568
  );
557
569
  if (!validation.valid) {
558
570
  return { data: void 0, error: validation.error };
@@ -560,24 +572,123 @@ class QueryBuilder {
560
572
  const stripped = validation.data.map(
561
573
  (record) => this.stripODataAnnotationsIfNeeded(record, options)
562
574
  );
563
- return {
564
- data: stripped,
565
- error: void 0
566
- };
575
+ return { data: stripped, error: void 0 };
567
576
  }
568
- } catch (error) {
577
+ }
578
+ if (this.isCountMode) {
579
+ const tableId2 = this.getTableId(mergedOptions.useEntityIds);
580
+ const result2 = await this.context._makeRequest(
581
+ `/${this.databaseName}/${tableId2}/$count${queryString}`,
582
+ mergedOptions
583
+ );
584
+ if (result2.error) {
585
+ return { data: void 0, error: result2.error };
586
+ }
587
+ const count = typeof result2.data === "string" ? Number(result2.data) : result2.data;
588
+ return { data: count, error: void 0 };
589
+ }
590
+ const tableId = this.getTableId(mergedOptions.useEntityIds);
591
+ const result = await this.context._makeRequest(
592
+ `/${this.databaseName}/${tableId}${queryString}`,
593
+ mergedOptions
594
+ );
595
+ if (result.error) {
596
+ return { data: void 0, error: result.error };
597
+ }
598
+ let response = result.data;
599
+ const shouldUseIds = mergedOptions.useEntityIds ?? false;
600
+ if (((_h = this.occurrence) == null ? void 0 : _h.baseTable) && shouldUseIds) {
601
+ const expandValidationConfigs2 = this.buildExpandValidationConfigs(
602
+ this.expandConfigs
603
+ );
604
+ response = transformResponseFields(
605
+ response,
606
+ this.occurrence.baseTable,
607
+ expandValidationConfigs2
608
+ );
609
+ }
610
+ if ((options == null ? void 0 : options.skipValidation) === true) {
611
+ const resp = response;
612
+ if (this.singleMode !== false) {
613
+ const records = resp.value ?? [resp];
614
+ const count = Array.isArray(records) ? records.length : 1;
615
+ if (count > 1) {
616
+ return {
617
+ data: void 0,
618
+ error: new RecordCountMismatchError(
619
+ this.singleMode === "exact" ? "one" : "at-most-one",
620
+ count
621
+ )
622
+ };
623
+ }
624
+ if (count === 0) {
625
+ if (this.singleMode === "exact") {
626
+ return {
627
+ data: void 0,
628
+ error: new RecordCountMismatchError("one", 0)
629
+ };
630
+ }
631
+ return { data: null, error: void 0 };
632
+ }
633
+ const record = Array.isArray(records) ? records[0] : records;
634
+ const stripped = this.stripODataAnnotationsIfNeeded(record, options);
635
+ return { data: stripped, error: void 0 };
636
+ } else {
637
+ const records = resp.value ?? [];
638
+ const stripped = records.map(
639
+ (record) => this.stripODataAnnotationsIfNeeded(record, options)
640
+ );
641
+ return { data: stripped, error: void 0 };
642
+ }
643
+ }
644
+ const schema = (_j = (_i = this.occurrence) == null ? void 0 : _i.baseTable) == null ? void 0 : _j.schema;
645
+ const selectedFields = this.queryOptions.select;
646
+ const expandValidationConfigs = this.buildExpandValidationConfigs(
647
+ this.expandConfigs
648
+ );
649
+ if (this.singleMode !== false) {
650
+ const validation = await validateSingleResponse(
651
+ response,
652
+ schema,
653
+ selectedFields,
654
+ expandValidationConfigs,
655
+ this.singleMode
656
+ );
657
+ if (!validation.valid) {
658
+ return { data: void 0, error: validation.error };
659
+ }
660
+ const stripped = validation.data ? this.stripODataAnnotationsIfNeeded(validation.data, options) : null;
569
661
  return {
570
- data: void 0,
571
- error: error instanceof Error ? error : new Error(String(error))
662
+ data: stripped,
663
+ error: void 0
664
+ };
665
+ } else {
666
+ const validation = await validateListResponse(
667
+ response,
668
+ schema,
669
+ selectedFields,
670
+ expandValidationConfigs
671
+ );
672
+ if (!validation.valid) {
673
+ return { data: void 0, error: validation.error };
674
+ }
675
+ const stripped = validation.data.map(
676
+ (record) => this.stripODataAnnotationsIfNeeded(record, options)
677
+ );
678
+ return {
679
+ data: stripped,
680
+ error: void 0
572
681
  };
573
682
  }
574
683
  }
575
684
  getQueryString() {
685
+ var _a;
576
686
  const queryOptionsWithoutExpand = { ...this.queryOptions };
577
687
  delete queryOptionsWithoutExpand.expand;
578
688
  if (queryOptionsWithoutExpand.select) {
579
689
  queryOptionsWithoutExpand.select = this.formatSelectFields(
580
- queryOptionsWithoutExpand.select
690
+ queryOptionsWithoutExpand.select,
691
+ (_a = this.occurrence) == null ? void 0 : _a.baseTable
581
692
  );
582
693
  }
583
694
  let queryParams = buildQuery(queryOptionsWithoutExpand);
@@ -604,17 +715,24 @@ class QueryBuilder {
604
715
  return queryParams ? `${path}${queryParams}` : path;
605
716
  }
606
717
  if (this.isNavigate && !this.navigateRecordId && this.navigateRelation && this.navigateSourceTableName) {
607
- const path = `/${this.navigateSourceTableName}/${this.navigateRelation}`;
718
+ let path;
719
+ if (this.navigateBasePath) {
720
+ path = `/${this.navigateBasePath}/${this.navigateRelation}`;
721
+ } else {
722
+ path = `/${this.navigateSourceTableName}/${this.navigateRelation}`;
723
+ }
608
724
  return queryParams ? `${path}${queryParams}` : path;
609
725
  }
610
726
  return `/${this.tableName}${queryParams}`;
611
727
  }
612
728
  getRequestConfig() {
729
+ var _a;
613
730
  const queryOptionsWithoutExpand = { ...this.queryOptions };
614
731
  delete queryOptionsWithoutExpand.expand;
615
732
  if (queryOptionsWithoutExpand.select) {
616
733
  queryOptionsWithoutExpand.select = this.formatSelectFields(
617
- queryOptionsWithoutExpand.select
734
+ queryOptionsWithoutExpand.select,
735
+ (_a = this.occurrence) == null ? void 0 : _a.baseTable
618
736
  );
619
737
  }
620
738
  let queryString = buildQuery(queryOptionsWithoutExpand);
@@ -631,7 +749,11 @@ class QueryBuilder {
631
749
  url = `/${this.databaseName}/${this.navigateSourceTableName}('${this.navigateRecordId}')/${this.navigateRelation}${queryString}`;
632
750
  }
633
751
  } else if (this.isNavigate && !this.navigateRecordId && this.navigateRelation && this.navigateSourceTableName) {
634
- url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}${queryString}`;
752
+ if (this.navigateBasePath) {
753
+ url = `/${this.databaseName}/${this.navigateBasePath}/${this.navigateRelation}${queryString}`;
754
+ } else {
755
+ url = `/${this.databaseName}/${this.navigateSourceTableName}/${this.navigateRelation}${queryString}`;
756
+ }
635
757
  } else if (this.isCountMode) {
636
758
  url = `/${this.databaseName}/${this.tableName}/$count${queryString}`;
637
759
  } else {
@@ -642,6 +764,142 @@ class QueryBuilder {
642
764
  url
643
765
  };
644
766
  }
767
+ toRequest(baseUrl) {
768
+ const config = this.getRequestConfig();
769
+ const fullUrl = `${baseUrl}${config.url}`;
770
+ return new Request(fullUrl, {
771
+ method: config.method,
772
+ headers: {
773
+ "Content-Type": "application/json",
774
+ Accept: "application/json"
775
+ }
776
+ });
777
+ }
778
+ async processResponse(response, options) {
779
+ var _a, _b, _c;
780
+ if (response.status === 204) {
781
+ if (this.singleMode !== false) {
782
+ if (this.singleMode === "maybe") {
783
+ return { data: null, error: void 0 };
784
+ }
785
+ return {
786
+ data: void 0,
787
+ error: new RecordCountMismatchError("one", 0)
788
+ };
789
+ }
790
+ return { data: [], error: void 0 };
791
+ }
792
+ let rawData;
793
+ try {
794
+ rawData = await response.json();
795
+ } catch (err) {
796
+ if (err instanceof SyntaxError && response.status === 204) {
797
+ return { data: [], error: void 0 };
798
+ }
799
+ return {
800
+ data: void 0,
801
+ error: {
802
+ name: "ResponseParseError",
803
+ message: `Failed to parse response JSON: ${err instanceof Error ? err.message : "Unknown error"}`,
804
+ timestamp: /* @__PURE__ */ new Date()
805
+ }
806
+ };
807
+ }
808
+ if (!rawData) {
809
+ return {
810
+ data: void 0,
811
+ error: {
812
+ name: "ResponseError",
813
+ message: "Response body was empty or null",
814
+ timestamp: /* @__PURE__ */ new Date()
815
+ }
816
+ };
817
+ }
818
+ const shouldUseIds = (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds;
819
+ let transformedData = rawData;
820
+ if (((_a = this.occurrence) == null ? void 0 : _a.baseTable) && shouldUseIds) {
821
+ const expandValidationConfigs2 = this.buildExpandValidationConfigs(
822
+ this.expandConfigs
823
+ );
824
+ transformedData = transformResponseFields(
825
+ rawData,
826
+ this.occurrence.baseTable,
827
+ expandValidationConfigs2
828
+ );
829
+ }
830
+ if ((options == null ? void 0 : options.skipValidation) === true) {
831
+ const resp = transformedData;
832
+ if (this.singleMode !== false) {
833
+ const records = resp.value ?? [resp];
834
+ const count = Array.isArray(records) ? records.length : 1;
835
+ if (count > 1) {
836
+ return {
837
+ data: void 0,
838
+ error: new RecordCountMismatchError(
839
+ this.singleMode === "exact" ? "one" : "at-most-one",
840
+ count
841
+ )
842
+ };
843
+ }
844
+ if (count === 0) {
845
+ if (this.singleMode === "exact") {
846
+ return {
847
+ data: void 0,
848
+ error: new RecordCountMismatchError("one", 0)
849
+ };
850
+ }
851
+ return { data: null, error: void 0 };
852
+ }
853
+ const record = Array.isArray(records) ? records[0] : records;
854
+ const stripped2 = this.stripODataAnnotationsIfNeeded(record, options);
855
+ return { data: stripped2, error: void 0 };
856
+ } else {
857
+ const records = resp.value ?? [];
858
+ const stripped2 = records.map(
859
+ (record) => this.stripODataAnnotationsIfNeeded(record, options)
860
+ );
861
+ return { data: stripped2, error: void 0 };
862
+ }
863
+ }
864
+ const schema = (_c = (_b = this.occurrence) == null ? void 0 : _b.baseTable) == null ? void 0 : _c.schema;
865
+ const selectedFields = this.queryOptions.select;
866
+ const expandValidationConfigs = this.buildExpandValidationConfigs(
867
+ this.expandConfigs
868
+ );
869
+ if (this.singleMode !== false) {
870
+ const validation2 = await validateSingleResponse(
871
+ transformedData,
872
+ schema,
873
+ selectedFields,
874
+ expandValidationConfigs,
875
+ this.singleMode
876
+ );
877
+ if (!validation2.valid) {
878
+ return { data: void 0, error: validation2.error };
879
+ }
880
+ if (validation2.data === null) {
881
+ return { data: null, error: void 0 };
882
+ }
883
+ const stripped2 = this.stripODataAnnotationsIfNeeded(
884
+ validation2.data,
885
+ options
886
+ );
887
+ return { data: stripped2, error: void 0 };
888
+ }
889
+ const validation = await validateListResponse(
890
+ transformedData,
891
+ schema,
892
+ selectedFields,
893
+ expandValidationConfigs
894
+ );
895
+ if (!validation.valid) {
896
+ return { data: void 0, error: validation.error };
897
+ }
898
+ const stripped = validation.data.map(
899
+ (record) => this.stripODataAnnotationsIfNeeded(record, options)
900
+ );
901
+ return { data: stripped, error: void 0 };
902
+ }
645
903
  }
646
904
  export {
647
905
  QueryBuilder