@proofkit/fmodata 0.1.0-alpha.6 → 0.1.0-alpha.7

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 (61) hide show
  1. package/README.md +333 -3
  2. package/dist/esm/client/batch-builder.d.ts +54 -0
  3. package/dist/esm/client/batch-builder.js +179 -0
  4. package/dist/esm/client/batch-builder.js.map +1 -0
  5. package/dist/esm/client/batch-request.d.ts +61 -0
  6. package/dist/esm/client/batch-request.js +252 -0
  7. package/dist/esm/client/batch-request.js.map +1 -0
  8. package/dist/esm/client/database.d.ts +43 -11
  9. package/dist/esm/client/database.js +64 -10
  10. package/dist/esm/client/database.js.map +1 -1
  11. package/dist/esm/client/delete-builder.d.ts +21 -2
  12. package/dist/esm/client/delete-builder.js +76 -9
  13. package/dist/esm/client/delete-builder.js.map +1 -1
  14. package/dist/esm/client/entity-set.d.ts +15 -4
  15. package/dist/esm/client/entity-set.js +23 -7
  16. package/dist/esm/client/entity-set.js.map +1 -1
  17. package/dist/esm/client/filemaker-odata.d.ts +11 -5
  18. package/dist/esm/client/filemaker-odata.js +46 -14
  19. package/dist/esm/client/filemaker-odata.js.map +1 -1
  20. package/dist/esm/client/insert-builder.d.ts +38 -3
  21. package/dist/esm/client/insert-builder.js +195 -9
  22. package/dist/esm/client/insert-builder.js.map +1 -1
  23. package/dist/esm/client/query-builder.d.ts +19 -3
  24. package/dist/esm/client/query-builder.js +193 -17
  25. package/dist/esm/client/query-builder.js.map +1 -1
  26. package/dist/esm/client/record-builder.d.ts +17 -2
  27. package/dist/esm/client/record-builder.js +87 -5
  28. package/dist/esm/client/record-builder.js.map +1 -1
  29. package/dist/esm/client/response-processor.d.ts +38 -0
  30. package/dist/esm/client/schema-manager.d.ts +57 -0
  31. package/dist/esm/client/schema-manager.js +132 -0
  32. package/dist/esm/client/schema-manager.js.map +1 -0
  33. package/dist/esm/client/update-builder.d.ts +34 -11
  34. package/dist/esm/client/update-builder.js +119 -19
  35. package/dist/esm/client/update-builder.js.map +1 -1
  36. package/dist/esm/errors.d.ts +14 -1
  37. package/dist/esm/errors.js +26 -0
  38. package/dist/esm/errors.js.map +1 -1
  39. package/dist/esm/index.d.ts +3 -2
  40. package/dist/esm/index.js +3 -1
  41. package/dist/esm/transform.d.ts +9 -0
  42. package/dist/esm/transform.js +7 -0
  43. package/dist/esm/transform.js.map +1 -1
  44. package/dist/esm/types.d.ts +69 -1
  45. package/package.json +1 -1
  46. package/src/client/batch-builder.ts +265 -0
  47. package/src/client/batch-request.ts +485 -0
  48. package/src/client/database.ts +106 -52
  49. package/src/client/delete-builder.ts +116 -14
  50. package/src/client/entity-set.ts +80 -6
  51. package/src/client/filemaker-odata.ts +65 -19
  52. package/src/client/insert-builder.ts +296 -18
  53. package/src/client/query-builder.ts +278 -17
  54. package/src/client/record-builder.ts +119 -11
  55. package/src/client/response-processor.ts +103 -0
  56. package/src/client/schema-manager.ts +246 -0
  57. package/src/client/update-builder.ts +195 -37
  58. package/src/errors.ts +33 -1
  59. package/src/index.ts +13 -0
  60. package/src/transform.ts +19 -6
  61. package/src/types.ts +89 -1
@@ -2,16 +2,19 @@ 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 { getTableIdentifiers } from "../transform.js";
5
6
  class DeleteBuilder {
6
7
  constructor(config) {
7
8
  __publicField(this, "tableName");
8
9
  __publicField(this, "databaseName");
9
10
  __publicField(this, "context");
10
11
  __publicField(this, "occurrence");
12
+ __publicField(this, "databaseUseEntityIds");
11
13
  this.occurrence = config.occurrence;
12
14
  this.tableName = config.tableName;
13
15
  this.databaseName = config.databaseName;
14
16
  this.context = config.context;
17
+ this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
15
18
  }
16
19
  /**
17
20
  * Delete a single record by ID
@@ -23,7 +26,8 @@ class DeleteBuilder {
23
26
  databaseName: this.databaseName,
24
27
  context: this.context,
25
28
  mode: "byId",
26
- recordId: id
29
+ recordId: id,
30
+ databaseUseEntityIds: this.databaseUseEntityIds
27
31
  });
28
32
  }
29
33
  /**
@@ -44,7 +48,8 @@ class DeleteBuilder {
44
48
  databaseName: this.databaseName,
45
49
  context: this.context,
46
50
  mode: "byFilter",
47
- queryBuilder: configuredBuilder
51
+ queryBuilder: configuredBuilder,
52
+ databaseUseEntityIds: this.databaseUseEntityIds
48
53
  });
49
54
  }
50
55
  }
@@ -57,6 +62,7 @@ class ExecutableDeleteBuilder {
57
62
  __publicField(this, "mode");
58
63
  __publicField(this, "recordId");
59
64
  __publicField(this, "queryBuilder");
65
+ __publicField(this, "databaseUseEntityIds");
60
66
  this.occurrence = config.occurrence;
61
67
  this.tableName = config.tableName;
62
68
  this.databaseName = config.databaseName;
@@ -64,22 +70,56 @@ class ExecutableDeleteBuilder {
64
70
  this.mode = config.mode;
65
71
  this.recordId = config.recordId;
66
72
  this.queryBuilder = config.queryBuilder;
73
+ this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;
74
+ }
75
+ /**
76
+ * Helper to merge database-level useEntityIds with per-request options
77
+ */
78
+ mergeExecuteOptions(options) {
79
+ return {
80
+ ...options,
81
+ useEntityIds: (options == null ? void 0 : options.useEntityIds) ?? this.databaseUseEntityIds
82
+ };
83
+ }
84
+ /**
85
+ * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
86
+ * @param useEntityIds - Optional override for entity ID usage
87
+ */
88
+ getTableId(useEntityIds) {
89
+ var _a, _b;
90
+ if (!this.occurrence) {
91
+ return this.tableName;
92
+ }
93
+ const contextDefault = ((_b = (_a = this.context)._getUseEntityIds) == null ? void 0 : _b.call(_a)) ?? false;
94
+ const shouldUseIds = useEntityIds ?? contextDefault;
95
+ if (shouldUseIds) {
96
+ const identifiers = getTableIdentifiers(this.occurrence);
97
+ if (!identifiers.id) {
98
+ throw new Error(
99
+ `useEntityIds is true but TableOccurrence "${identifiers.name}" does not have an fmtId defined`
100
+ );
101
+ }
102
+ return identifiers.id;
103
+ }
104
+ return this.occurrence.getTableName();
67
105
  }
68
106
  async execute(options) {
107
+ const mergedOptions = this.mergeExecuteOptions(options);
108
+ const tableId = this.getTableId(mergedOptions.useEntityIds);
69
109
  let url;
70
110
  if (this.mode === "byId") {
71
- url = `/${this.databaseName}/${this.tableName}('${this.recordId}')`;
111
+ url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
72
112
  } else {
73
113
  if (!this.queryBuilder) {
74
114
  throw new Error("Query builder is required for filter-based delete");
75
115
  }
76
116
  const queryString = this.queryBuilder.getQueryString();
77
- const queryParams = queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
78
- url = `/${this.databaseName}/${this.tableName}${queryParams}`;
117
+ const queryParams = queryString.startsWith(`/${tableId}`) ? queryString.slice(`/${tableId}`.length) : queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
118
+ url = `/${this.databaseName}/${tableId}${queryParams}`;
79
119
  }
80
120
  const result = await this.context._makeRequest(url, {
81
121
  method: "DELETE",
82
- ...options
122
+ ...mergedOptions
83
123
  });
84
124
  if (result.error) {
85
125
  return { data: void 0, error: result.error };
@@ -94,22 +134,49 @@ class ExecutableDeleteBuilder {
94
134
  return { data: { deletedCount }, error: void 0 };
95
135
  }
96
136
  getRequestConfig() {
137
+ const tableId = this.getTableId(this.databaseUseEntityIds);
97
138
  let url;
98
139
  if (this.mode === "byId") {
99
- url = `/${this.databaseName}/${this.tableName}('${this.recordId}')`;
140
+ url = `/${this.databaseName}/${tableId}('${this.recordId}')`;
100
141
  } else {
101
142
  if (!this.queryBuilder) {
102
143
  throw new Error("Query builder is required for filter-based delete");
103
144
  }
104
145
  const queryString = this.queryBuilder.getQueryString();
105
- const queryParams = queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
106
- url = `/${this.databaseName}/${this.tableName}${queryParams}`;
146
+ const queryParams = queryString.startsWith(`/${tableId}`) ? queryString.slice(`/${tableId}`.length) : queryString.startsWith(`/${this.tableName}`) ? queryString.slice(`/${this.tableName}`.length) : queryString;
147
+ url = `/${this.databaseName}/${tableId}${queryParams}`;
107
148
  }
108
149
  return {
109
150
  method: "DELETE",
110
151
  url
111
152
  };
112
153
  }
154
+ toRequest(baseUrl) {
155
+ const config = this.getRequestConfig();
156
+ const fullUrl = `${baseUrl}${config.url}`;
157
+ return new Request(fullUrl, {
158
+ method: config.method,
159
+ headers: {
160
+ Accept: "application/json"
161
+ }
162
+ });
163
+ }
164
+ async processResponse(response, options) {
165
+ const text = await response.text();
166
+ if (!text || text.trim() === "") {
167
+ const affectedRows = response.headers.get("fmodata.affected_rows");
168
+ const deletedCount2 = affectedRows ? parseInt(affectedRows, 10) : 1;
169
+ return { data: { deletedCount: deletedCount2 }, error: void 0 };
170
+ }
171
+ const rawResponse = JSON.parse(text);
172
+ let deletedCount = 0;
173
+ if (typeof rawResponse === "number") {
174
+ deletedCount = rawResponse;
175
+ } else if (rawResponse && typeof rawResponse === "object") {
176
+ deletedCount = rawResponse.deletedCount || 0;
177
+ }
178
+ return { data: { deletedCount }, error: void 0 };
179
+ }
113
180
  }
114
181
  export {
115
182
  DeleteBuilder,
@@ -1 +1 @@
1
- {"version":3,"file":"delete-builder.js","sources":["../../../src/client/delete-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport buildQuery from \"odata-query\";\n\n/**\n * Initial delete builder returned from EntitySet.delete()\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class DeleteBuilder<T extends Record<string, any>> {\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n }\n\n /**\n * Delete a single record by ID\n */\n byId(id: string | number): ExecutableDeleteBuilder<T> {\n return new ExecutableDeleteBuilder<T>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n mode: \"byId\",\n recordId: id,\n });\n }\n\n /**\n * Delete records matching a filter query\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 ): ExecutableDeleteBuilder<T> {\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 ExecutableDeleteBuilder<T>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n });\n }\n}\n\n/**\n * Executable delete builder - has execute() method\n * Returned after calling .byId() or .where()\n */\nexport class ExecutableDeleteBuilder<T extends Record<string, any>>\n implements ExecutableBuilder<{ deletedCount: number }>\n{\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\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 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.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<{ deletedCount: number }>> {\n let url: string;\n\n if (this.mode === \"byId\") {\n // Delete single record by ID: DELETE /{database}/{table}('id')\n url = `/${this.databaseName}/${this.tableName}('${this.recordId}')`;\n } else {\n // Delete by filter: DELETE /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based delete\");\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 DELETE request\n const result = await this.context._makeRequest(url, {\n method: \"DELETE\",\n ...options,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // OData returns 204 No Content with fmodata.affected_rows header\n // The _makeRequest should handle extracting the header value\n // For now, we'll check if response contains the count\n let deletedCount = 0;\n\n if (typeof response === \"number\") {\n deletedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n deletedCount = (response as any).deletedCount || 0;\n }\n\n return { data: { deletedCount }, error: undefined };\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 delete\");\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: \"DELETE\",\n url,\n };\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"delete-builder.js","sources":["../../../src/client/delete-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n WithSystemFields,\n ExecuteOptions,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\nimport { getTableIdentifiers } from \"../transform\";\n\n/**\n * Initial delete builder returned from EntitySet.delete()\n * Requires calling .byId() or .where() before .execute() is available\n */\nexport class DeleteBuilder<T extends Record<string, any>> {\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n databaseUseEntityIds?: boolean;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Delete a single record by ID\n */\n byId(id: string | number): ExecutableDeleteBuilder<T> {\n return new ExecutableDeleteBuilder<T>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n mode: \"byId\",\n recordId: id,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n\n /**\n * Delete records matching a filter query\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 ): ExecutableDeleteBuilder<T> {\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 ExecutableDeleteBuilder<T>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n mode: \"byFilter\",\n queryBuilder: configuredBuilder,\n databaseUseEntityIds: this.databaseUseEntityIds,\n });\n }\n}\n\n/**\n * Executable delete builder - has execute() method\n * Returned after calling .byId() or .where()\n */\nexport class ExecutableDeleteBuilder<T extends Record<string, any>>\n implements ExecutableBuilder<{ deletedCount: number }>\n{\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private occurrence?: TableOccurrence<any, any, any, any>;\n private mode: \"byId\" | \"byFilter\";\n private recordId?: string | number;\n private queryBuilder?: QueryBuilder<any>;\n private databaseUseEntityIds: boolean;\n\n constructor(config: {\n occurrence?: TableOccurrence<any, any, any, any>;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n mode: \"byId\" | \"byFilter\";\n recordId?: string | number;\n queryBuilder?: QueryBuilder<any>;\n databaseUseEntityIds?: boolean;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.mode = config.mode;\n this.recordId = config.recordId;\n this.queryBuilder = config.queryBuilder;\n this.databaseUseEntityIds = config.databaseUseEntityIds ?? false;\n }\n\n /**\n * Helper to merge database-level useEntityIds with per-request options\n */\n private mergeExecuteOptions(\n options?: RequestInit & FFetchOptions & ExecuteOptions,\n ): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n // If useEntityIds is not set in options, use the database-level setting\n return {\n ...options,\n useEntityIds: options?.useEntityIds ?? this.databaseUseEntityIds,\n };\n }\n\n /**\n * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name\n * @param useEntityIds - Optional override for entity ID usage\n */\n private getTableId(useEntityIds?: boolean): string {\n if (!this.occurrence) {\n return this.tableName;\n }\n\n const contextDefault = this.context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIds ?? contextDefault;\n\n if (shouldUseIds) {\n const identifiers = getTableIdentifiers(this.occurrence);\n if (!identifiers.id) {\n throw new Error(\n `useEntityIds is true but TableOccurrence \"${identifiers.name}\" does not have an fmtId defined`,\n );\n }\n return identifiers.id;\n }\n\n return this.occurrence.getTableName();\n }\n\n async execute(\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<Result<{ deletedCount: number }>> {\n // Merge database-level useEntityIds with per-request options\n const mergedOptions = this.mergeExecuteOptions(options);\n\n // Get table identifier with override support\n const tableId = this.getTableId(mergedOptions.useEntityIds);\n\n let url: string;\n\n if (this.mode === \"byId\") {\n // Delete single record by ID: DELETE /{database}/{table}('id')\n url = `/${this.databaseName}/${tableId}('${this.recordId}')`;\n } else {\n // Delete by filter: DELETE /{database}/{table}?$filter=...\n if (!this.queryBuilder) {\n throw new Error(\"Query builder is required for filter-based delete\");\n }\n\n // Get the query string from the configured QueryBuilder\n const queryString = this.queryBuilder.getQueryString();\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 DELETE request\n const result = await this.context._makeRequest(url, {\n method: \"DELETE\",\n ...mergedOptions,\n });\n\n if (result.error) {\n return { data: undefined, error: result.error };\n }\n\n const response = result.data;\n\n // OData returns 204 No Content with fmodata.affected_rows header\n // The _makeRequest should handle extracting the header value\n // For now, we'll check if response contains the count\n let deletedCount = 0;\n\n if (typeof response === \"number\") {\n deletedCount = response;\n } else if (response && typeof response === \"object\") {\n // Check if the response has a count property (fallback)\n deletedCount = (response as any).deletedCount || 0;\n }\n\n return { data: { deletedCount }, error: undefined };\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n // For batch operations, use database-level setting (no per-request override available here)\n const tableId = this.getTableId(this.databaseUseEntityIds);\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 delete\");\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: \"DELETE\",\n url,\n };\n }\n\n toRequest(baseUrl: string): Request {\n const config = this.getRequestConfig();\n const fullUrl = `${baseUrl}${config.url}`;\n\n return new Request(fullUrl, {\n method: config.method,\n headers: {\n Accept: \"application/json\",\n },\n });\n }\n\n async processResponse(\n response: Response,\n options?: ExecuteOptions,\n ): Promise<Result<{ deletedCount: number }>> {\n // Check for empty response (204 No Content)\n const text = await response.text();\n if (!text || text.trim() === \"\") {\n // For 204 No Content, check the fmodata.affected_rows header\n const affectedRows = response.headers.get(\"fmodata.affected_rows\");\n const deletedCount = affectedRows ? parseInt(affectedRows, 10) : 1;\n return { data: { deletedCount }, error: undefined };\n }\n\n const rawResponse = JSON.parse(text);\n\n // OData returns 204 No Content with fmodata.affected_rows header\n // The _makeRequest should handle extracting the header value\n // For now, we'll check if response contains the count\n let deletedCount = 0;\n\n if (typeof rawResponse === \"number\") {\n deletedCount = rawResponse;\n } else if (rawResponse && typeof rawResponse === \"object\") {\n // Check if the response has a count property (fallback)\n deletedCount = (rawResponse as any).deletedCount || 0;\n }\n\n return { data: { deletedCount }, error: undefined };\n }\n}\n"],"names":["deletedCount"],"mappings":"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"}
@@ -7,6 +7,7 @@ import { RecordBuilder } from './record-builder.js';
7
7
  import { InsertBuilder } from './insert-builder.js';
8
8
  import { DeleteBuilder } from './delete-builder.js';
9
9
  import { UpdateBuilder } from './update-builder.js';
10
+ import { Database } from './database.js';
10
11
  type ExtractNavigationNames<O extends TableOccurrence<any, any, any, any> | undefined> = O extends TableOccurrence<any, any, infer Nav, any> ? Nav extends Record<string, any> ? keyof Nav & string : never : never;
11
12
  type ExtractSchemaFromOccurrence<O> = O extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<infer S, any> ? S : never : never;
12
13
  type ExtractDefaultSelect<O> = O extends TableOccurrence<infer BT, any, any, infer DefSelect> ? BT extends BaseTable<infer S, any> ? DefSelect extends "all" ? keyof S : DefSelect extends "schema" ? keyof S : DefSelect extends readonly (infer K)[] ? K & keyof S : keyof S : never : never;
@@ -17,7 +18,7 @@ export declare class EntitySet<Schema extends Record<string, StandardSchemaV1> =
17
18
  private tableName;
18
19
  private databaseName;
19
20
  private context;
20
- private database?;
21
+ private database;
21
22
  private isNavigateFromEntitySet?;
22
23
  private navigateRelation?;
23
24
  private navigateSourceTableName?;
@@ -33,12 +34,22 @@ export declare class EntitySet<Schema extends Record<string, StandardSchemaV1> =
33
34
  tableName: string;
34
35
  databaseName: string;
35
36
  context: ExecutionContext;
36
- database?: any;
37
+ database: Database<any>;
37
38
  }): EntitySet<OccurrenceSchema, Occ>;
38
39
  list(): QueryBuilder<InferSchemaType<Schema>, Occ extends TableOccurrence<any, any, any, any> ? ExtractDefaultSelect<Occ> : keyof InferSchemaType<Schema>, false, false, Occ>;
39
40
  get(id: string | number): RecordBuilder<InferSchemaType<Schema>, false, keyof InferSchemaType<Schema>, Occ>;
40
- insert(data: Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? InsertData<BT> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>>): InsertBuilder<InferSchemaType<Schema>, Occ>;
41
- update(data: Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? UpdateData<BT> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>>): UpdateBuilder<InferSchemaType<Schema>, Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? BT : BaseTable<Schema, any, any, any> : BaseTable<Schema, any, any, any>>;
41
+ insert(data: Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? InsertData<BT> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>>, options: {
42
+ returnFullRecord: false;
43
+ }): InsertBuilder<InferSchemaType<Schema>, Occ, "minimal">;
44
+ insert(data: Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? InsertData<BT> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>>, options?: {
45
+ returnFullRecord?: true;
46
+ }): InsertBuilder<InferSchemaType<Schema>, Occ, "representation">;
47
+ update(data: Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? UpdateData<BT> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>>, options: {
48
+ returnFullRecord: true;
49
+ }): UpdateBuilder<InferSchemaType<Schema>, Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? BT : BaseTable<Schema, any, any, any> : BaseTable<Schema, any, any, any>, "representation">;
50
+ update(data: Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? UpdateData<BT> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>>, options?: {
51
+ returnFullRecord?: false;
52
+ }): UpdateBuilder<InferSchemaType<Schema>, Occ extends TableOccurrence<infer BT, any, any, any> ? BT extends BaseTable<any, any, any, any> ? BT : BaseTable<Schema, any, any, any> : BaseTable<Schema, any, any, any>, "minimal">;
42
53
  delete(): DeleteBuilder<InferSchemaType<Schema>>;
43
54
  navigate<RelationName extends ExtractNavigationNames<Occ>>(relationName: RelationName): EntitySet<ExtractSchemaFromOccurrence<FindNavigationTarget<Occ, RelationName>> extends Record<string, StandardSchemaV1> ? ExtractSchemaFromOccurrence<FindNavigationTarget<Occ, RelationName>> : Record<string, StandardSchemaV1>, FindNavigationTarget<Occ, RelationName>>;
44
55
  navigate(relationName: string): EntitySet<Record<string, StandardSchemaV1>, undefined>;
@@ -34,11 +34,13 @@ class EntitySet {
34
34
  });
35
35
  }
36
36
  list() {
37
+ var _a;
37
38
  const builder = new QueryBuilder({
38
39
  occurrence: this.occurrence,
39
40
  tableName: this.tableName,
40
41
  databaseName: this.databaseName,
41
- context: this.context
42
+ context: this.context,
43
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
42
44
  });
43
45
  if (this.occurrence) {
44
46
  const defaultSelect = this.occurrence.defaultSelect;
@@ -62,12 +64,14 @@ class EntitySet {
62
64
  return builder;
63
65
  }
64
66
  get(id) {
67
+ var _a;
65
68
  const builder = new RecordBuilder({
66
69
  occurrence: this.occurrence,
67
70
  tableName: this.tableName,
68
71
  databaseName: this.databaseName,
69
72
  context: this.context,
70
- recordId: id
73
+ recordId: id,
74
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
71
75
  });
72
76
  if (this.isNavigateFromEntitySet) {
73
77
  builder.isNavigateFromEntitySet = true;
@@ -76,30 +80,42 @@ class EntitySet {
76
80
  }
77
81
  return builder;
78
82
  }
79
- insert(data) {
83
+ // Implementation
84
+ insert(data, options) {
85
+ var _a;
86
+ const returnPref = (options == null ? void 0 : options.returnFullRecord) === false ? "minimal" : "representation";
80
87
  return new InsertBuilder({
81
88
  occurrence: this.occurrence,
82
89
  tableName: this.tableName,
83
90
  databaseName: this.databaseName,
84
91
  context: this.context,
85
- data
92
+ data,
93
+ returnPreference: returnPref,
94
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
86
95
  });
87
96
  }
88
- update(data) {
97
+ // Implementation
98
+ update(data, options) {
99
+ var _a;
100
+ const returnPref = (options == null ? void 0 : options.returnFullRecord) === true ? "representation" : "minimal";
89
101
  return new UpdateBuilder({
90
102
  occurrence: this.occurrence,
91
103
  tableName: this.tableName,
92
104
  databaseName: this.databaseName,
93
105
  context: this.context,
94
- data
106
+ data,
107
+ returnPreference: returnPref,
108
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
95
109
  });
96
110
  }
97
111
  delete() {
112
+ var _a;
98
113
  return new DeleteBuilder({
99
114
  occurrence: this.occurrence,
100
115
  tableName: this.tableName,
101
116
  databaseName: this.databaseName,
102
- context: this.context
117
+ context: this.context,
118
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
103
119
  });
104
120
  }
105
121
  // Implementation
@@ -1 +1 @@
1
- {"version":3,"file":"entity-set.js","sources":["../../../src/client/entity-set.ts"],"sourcesContent":["import type {\n ExecutionContext,\n InferSchemaType,\n WithSystemFields,\n InsertData,\n UpdateData,\n} from \"../types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { BaseTable } from \"./base-table\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { RecordBuilder } from \"./record-builder\";\nimport { InsertBuilder } from \"./insert-builder\";\nimport { DeleteBuilder } from \"./delete-builder\";\nimport { UpdateBuilder } from \"./update-builder\";\n\n// Helper type to extract navigation relation names from an occurrence\ntype ExtractNavigationNames<\n O extends TableOccurrence<any, any, any, any> | undefined,\n> =\n O extends TableOccurrence<any, any, infer Nav, any>\n ? Nav extends Record<string, any>\n ? keyof Nav & string\n : never\n : never;\n\n// Helper type to extract schema from a TableOccurrence\ntype ExtractSchemaFromOccurrence<O> =\n O extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<infer S, any>\n ? S\n : never\n : never;\n\n// Helper type to extract defaultSelect from a TableOccurrence\ntype ExtractDefaultSelect<O> =\n O extends TableOccurrence<infer BT, any, any, infer DefSelect>\n ? BT extends BaseTable<infer S, any>\n ? DefSelect extends \"all\"\n ? keyof S\n : DefSelect extends \"schema\"\n ? keyof S\n : DefSelect extends readonly (infer K)[]\n ? K & keyof S\n : keyof S\n : never\n : never;\n\n// Helper type to resolve a navigation item (handles both direct and lazy-loaded)\ntype ResolveNavigationItem<T> = T extends () => infer R ? R : T;\n\n// Helper type to find target occurrence by relation name\ntype FindNavigationTarget<\n O extends TableOccurrence<any, any, any, any> | undefined,\n Name extends string,\n> =\n O extends TableOccurrence<any, any, infer Nav, any>\n ? Nav extends Record<string, any>\n ? Name extends keyof Nav\n ? ResolveNavigationItem<Nav[Name]>\n : TableOccurrence<\n BaseTable<Record<string, StandardSchemaV1>, any, any, any>,\n any,\n any,\n any\n >\n : TableOccurrence<\n BaseTable<Record<string, StandardSchemaV1>, any, any, any>,\n any,\n any,\n any\n >\n : TableOccurrence<\n BaseTable<Record<string, StandardSchemaV1>, any, any, any>,\n any,\n any,\n any\n >;\n\n// Helper type to get the inferred schema type from a target occurrence\ntype GetTargetSchemaType<\n O extends TableOccurrence<any, any, any, any> | undefined,\n Rel extends string,\n> = [FindNavigationTarget<O, Rel>] extends [\n TableOccurrence<infer BT, any, any, any>,\n]\n ? [BT] extends [BaseTable<infer S, any>]\n ? [S] extends [Record<string, StandardSchemaV1>]\n ? InferSchemaType<S>\n : Record<string, any>\n : Record<string, any>\n : Record<string, any>;\n\nexport class EntitySet<\n Schema extends Record<string, StandardSchemaV1> = any,\n Occ extends TableOccurrence<any, any, any, any> | undefined = undefined,\n> {\n private occurrence?: Occ;\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private database?: any; // Database instance for accessing occurrences\n private isNavigateFromEntitySet?: boolean;\n private navigateRelation?: string;\n private navigateSourceTableName?: string;\n\n constructor(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n database?: any;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.database = config.database;\n }\n\n // Type-only method to help TypeScript infer the schema from occurrence\n static create<\n OccurrenceSchema extends Record<string, StandardSchemaV1>,\n Occ extends\n | TableOccurrence<\n BaseTable<OccurrenceSchema, any, any, any>,\n any,\n any,\n any\n >\n | undefined = undefined,\n >(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n database?: any;\n }): EntitySet<OccurrenceSchema, Occ> {\n return new EntitySet<OccurrenceSchema, Occ>({\n occurrence: config.occurrence,\n tableName: config.tableName,\n databaseName: config.databaseName,\n context: config.context,\n database: config.database,\n });\n }\n\n list(): QueryBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<any, any, any, any>\n ? ExtractDefaultSelect<Occ>\n : keyof InferSchemaType<Schema>,\n false,\n false,\n Occ\n > {\n const builder = new QueryBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<any, any, any, any>\n ? ExtractDefaultSelect<Occ>\n : keyof InferSchemaType<Schema>,\n false,\n false,\n Occ\n >({\n occurrence: this.occurrence as Occ,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n });\n\n // Apply defaultSelect if occurrence exists and select hasn't been called\n if (this.occurrence) {\n const defaultSelect = this.occurrence.defaultSelect;\n\n if (defaultSelect === \"schema\") {\n // Extract field names from schema\n const schema = this.occurrence.baseTable.schema;\n const fields = Object.keys(schema) as (keyof InferSchemaType<Schema>)[];\n // Deduplicate fields (same as select method)\n const uniqueFields = [...new Set(fields)];\n return builder.select(...uniqueFields);\n } else if (Array.isArray(defaultSelect)) {\n // Use the provided field names, deduplicated\n const uniqueFields = [\n ...new Set(defaultSelect),\n ] as (keyof InferSchemaType<Schema>)[];\n return builder.select(...uniqueFields);\n }\n // If defaultSelect is \"all\", no changes needed (current behavior)\n }\n\n // Propagate navigation context if present\n if (this.isNavigateFromEntitySet) {\n (builder as any).isNavigate = true;\n (builder as any).navigateRelation = this.navigateRelation;\n (builder as any).navigateSourceTableName = this.navigateSourceTableName;\n // navigateRecordId is intentionally not set (undefined) to indicate navigation from EntitySet\n }\n return builder;\n }\n\n get(\n id: string | number,\n ): RecordBuilder<\n InferSchemaType<Schema>,\n false,\n keyof InferSchemaType<Schema>,\n Occ\n > {\n const builder = new RecordBuilder<\n InferSchemaType<Schema>,\n false,\n keyof InferSchemaType<Schema>,\n Occ\n >({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n recordId: id,\n });\n // Propagate navigation context if present\n if (this.isNavigateFromEntitySet) {\n (builder as any).isNavigateFromEntitySet = true;\n (builder as any).navigateRelation = this.navigateRelation;\n (builder as any).navigateSourceTableName = this.navigateSourceTableName;\n }\n return builder;\n }\n\n insert(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? InsertData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n ): InsertBuilder<InferSchemaType<Schema>, Occ> {\n return new InsertBuilder<InferSchemaType<Schema>, Occ>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: data as Partial<InferSchemaType<Schema>>,\n });\n }\n\n update(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? UpdateData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n ): UpdateBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? BT\n : BaseTable<Schema, any, any, any>\n : BaseTable<Schema, any, any, any>\n > {\n return new UpdateBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? BT\n : BaseTable<Schema, any, any, any>\n : BaseTable<Schema, any, any, any>\n >({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: data as Partial<InferSchemaType<Schema>>,\n });\n }\n\n delete(): DeleteBuilder<InferSchemaType<Schema>> {\n return new DeleteBuilder<InferSchemaType<Schema>>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n });\n }\n\n // Overload for valid relation names - returns typed EntitySet\n navigate<RelationName extends ExtractNavigationNames<Occ>>(\n relationName: RelationName,\n ): EntitySet<\n ExtractSchemaFromOccurrence<\n FindNavigationTarget<Occ, RelationName>\n > extends Record<string, StandardSchemaV1>\n ? ExtractSchemaFromOccurrence<FindNavigationTarget<Occ, RelationName>>\n : Record<string, StandardSchemaV1>,\n FindNavigationTarget<Occ, RelationName>\n >;\n // Overload for arbitrary strings - returns generic EntitySet\n navigate(\n relationName: string,\n ): EntitySet<Record<string, StandardSchemaV1>, undefined>;\n // Implementation\n navigate(relationName: string): EntitySet<any, any> {\n // Use the target occurrence if available, otherwise allow untyped navigation\n // (useful when types might be incomplete)\n const targetOccurrence = this.occurrence?.navigation[relationName];\n const entitySet = new EntitySet<any, any>({\n occurrence: targetOccurrence,\n tableName: targetOccurrence?.name ?? relationName,\n databaseName: this.databaseName,\n context: this.context,\n });\n // Store the navigation info in the EntitySet\n // We'll need to pass this through when creating QueryBuilders\n (entitySet as any).isNavigateFromEntitySet = true;\n (entitySet as any).navigateRelation = relationName;\n (entitySet as any).navigateSourceTableName = this.tableName;\n return entitySet;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-set.js","sources":["../../../src/client/entity-set.ts"],"sourcesContent":["import type {\n ExecutionContext,\n InferSchemaType,\n WithSystemFields,\n InsertData,\n UpdateData,\n} from \"../types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { BaseTable } from \"./base-table\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { QueryBuilder } from \"./query-builder\";\nimport { RecordBuilder } from \"./record-builder\";\nimport { InsertBuilder } from \"./insert-builder\";\nimport { DeleteBuilder } from \"./delete-builder\";\nimport { UpdateBuilder } from \"./update-builder\";\nimport { Database } from \"./database\";\n\n// Helper type to extract navigation relation names from an occurrence\ntype ExtractNavigationNames<\n O extends TableOccurrence<any, any, any, any> | undefined,\n> =\n O extends TableOccurrence<any, any, infer Nav, any>\n ? Nav extends Record<string, any>\n ? keyof Nav & string\n : never\n : never;\n\n// Helper type to extract schema from a TableOccurrence\ntype ExtractSchemaFromOccurrence<O> =\n O extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<infer S, any>\n ? S\n : never\n : never;\n\n// Helper type to extract defaultSelect from a TableOccurrence\ntype ExtractDefaultSelect<O> =\n O extends TableOccurrence<infer BT, any, any, infer DefSelect>\n ? BT extends BaseTable<infer S, any>\n ? DefSelect extends \"all\"\n ? keyof S\n : DefSelect extends \"schema\"\n ? keyof S\n : DefSelect extends readonly (infer K)[]\n ? K & keyof S\n : keyof S\n : never\n : never;\n\n// Helper type to resolve a navigation item (handles both direct and lazy-loaded)\ntype ResolveNavigationItem<T> = T extends () => infer R ? R : T;\n\n// Helper type to find target occurrence by relation name\ntype FindNavigationTarget<\n O extends TableOccurrence<any, any, any, any> | undefined,\n Name extends string,\n> =\n O extends TableOccurrence<any, any, infer Nav, any>\n ? Nav extends Record<string, any>\n ? Name extends keyof Nav\n ? ResolveNavigationItem<Nav[Name]>\n : TableOccurrence<\n BaseTable<Record<string, StandardSchemaV1>, any, any, any>,\n any,\n any,\n any\n >\n : TableOccurrence<\n BaseTable<Record<string, StandardSchemaV1>, any, any, any>,\n any,\n any,\n any\n >\n : TableOccurrence<\n BaseTable<Record<string, StandardSchemaV1>, any, any, any>,\n any,\n any,\n any\n >;\n\n// Helper type to get the inferred schema type from a target occurrence\ntype GetTargetSchemaType<\n O extends TableOccurrence<any, any, any, any> | undefined,\n Rel extends string,\n> = [FindNavigationTarget<O, Rel>] extends [\n TableOccurrence<infer BT, any, any, any>,\n]\n ? [BT] extends [BaseTable<infer S, any>]\n ? [S] extends [Record<string, StandardSchemaV1>]\n ? InferSchemaType<S>\n : Record<string, any>\n : Record<string, any>\n : Record<string, any>;\n\nexport class EntitySet<\n Schema extends Record<string, StandardSchemaV1> = any,\n Occ extends TableOccurrence<any, any, any, any> | undefined = undefined,\n> {\n private occurrence?: Occ;\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private database: Database<any>; // Database instance for accessing occurrences\n private isNavigateFromEntitySet?: boolean;\n private navigateRelation?: string;\n private navigateSourceTableName?: string;\n\n constructor(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n database?: any;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.database = config.database;\n }\n\n // Type-only method to help TypeScript infer the schema from occurrence\n static create<\n OccurrenceSchema extends Record<string, StandardSchemaV1>,\n Occ extends\n | TableOccurrence<\n BaseTable<OccurrenceSchema, any, any, any>,\n any,\n any,\n any\n >\n | undefined = undefined,\n >(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n database: Database<any>;\n }): EntitySet<OccurrenceSchema, Occ> {\n return new EntitySet<OccurrenceSchema, Occ>({\n occurrence: config.occurrence,\n tableName: config.tableName,\n databaseName: config.databaseName,\n context: config.context,\n database: config.database,\n });\n }\n\n list(): QueryBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<any, any, any, any>\n ? ExtractDefaultSelect<Occ>\n : keyof InferSchemaType<Schema>,\n false,\n false,\n Occ\n > {\n const builder = new QueryBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<any, any, any, any>\n ? ExtractDefaultSelect<Occ>\n : keyof InferSchemaType<Schema>,\n false,\n false,\n Occ\n >({\n occurrence: this.occurrence as Occ,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.database?.isUsingEntityIds() ?? false,\n });\n\n // Apply defaultSelect if occurrence exists and select hasn't been called\n if (this.occurrence) {\n const defaultSelect = this.occurrence.defaultSelect;\n\n if (defaultSelect === \"schema\") {\n // Extract field names from schema\n const schema = this.occurrence.baseTable.schema;\n const fields = Object.keys(schema) as (keyof InferSchemaType<Schema>)[];\n // Deduplicate fields (same as select method)\n const uniqueFields = [...new Set(fields)];\n return builder.select(...uniqueFields);\n } else if (Array.isArray(defaultSelect)) {\n // Use the provided field names, deduplicated\n const uniqueFields = [\n ...new Set(defaultSelect),\n ] as (keyof InferSchemaType<Schema>)[];\n return builder.select(...uniqueFields);\n }\n // If defaultSelect is \"all\", no changes needed (current behavior)\n }\n\n // Propagate navigation context if present\n if (this.isNavigateFromEntitySet) {\n (builder as any).isNavigate = true;\n (builder as any).navigateRelation = this.navigateRelation;\n (builder as any).navigateSourceTableName = this.navigateSourceTableName;\n // navigateRecordId is intentionally not set (undefined) to indicate navigation from EntitySet\n }\n return builder;\n }\n\n get(\n id: string | number,\n ): RecordBuilder<\n InferSchemaType<Schema>,\n false,\n keyof InferSchemaType<Schema>,\n Occ\n > {\n const builder = new RecordBuilder<\n InferSchemaType<Schema>,\n false,\n keyof InferSchemaType<Schema>,\n Occ\n >({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n recordId: id,\n databaseUseEntityIds: this.database?.isUsingEntityIds() ?? false,\n });\n // Propagate navigation context if present\n if (this.isNavigateFromEntitySet) {\n (builder as any).isNavigateFromEntitySet = true;\n (builder as any).navigateRelation = this.navigateRelation;\n (builder as any).navigateSourceTableName = this.navigateSourceTableName;\n }\n return builder;\n }\n\n // Overload: when returnFullRecord is explicitly false\n insert(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? InsertData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n options: { returnFullRecord: false },\n ): InsertBuilder<InferSchemaType<Schema>, Occ, \"minimal\">;\n\n // Overload: when returnFullRecord is true or omitted (default)\n insert(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? InsertData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n options?: { returnFullRecord?: true },\n ): InsertBuilder<InferSchemaType<Schema>, Occ, \"representation\">;\n\n // Implementation\n insert(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? InsertData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n options?: { returnFullRecord?: boolean },\n ): InsertBuilder<InferSchemaType<Schema>, Occ, \"minimal\" | \"representation\"> {\n const returnPref =\n options?.returnFullRecord === false ? \"minimal\" : \"representation\";\n return new InsertBuilder<InferSchemaType<Schema>, Occ, typeof returnPref>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: data as Partial<InferSchemaType<Schema>>,\n returnPreference: returnPref as any,\n databaseUseEntityIds: this.database?.isUsingEntityIds() ?? false,\n });\n }\n\n // Overload: when returnFullRecord is explicitly true\n update(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? UpdateData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n options: { returnFullRecord: true },\n ): UpdateBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? BT\n : BaseTable<Schema, any, any, any>\n : BaseTable<Schema, any, any, any>,\n \"representation\"\n >;\n\n // Overload: when returnFullRecord is false or omitted (default returns count)\n update(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? UpdateData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n options?: { returnFullRecord?: false },\n ): UpdateBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? BT\n : BaseTable<Schema, any, any, any>\n : BaseTable<Schema, any, any, any>,\n \"minimal\"\n >;\n\n // Implementation\n update(\n data: Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? UpdateData<BT>\n : Partial<InferSchemaType<Schema>>\n : Partial<InferSchemaType<Schema>>,\n options?: { returnFullRecord?: boolean },\n ): UpdateBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? BT\n : BaseTable<Schema, any, any, any>\n : BaseTable<Schema, any, any, any>,\n \"minimal\" | \"representation\"\n > {\n const returnPref =\n options?.returnFullRecord === true ? \"representation\" : \"minimal\";\n return new UpdateBuilder<\n InferSchemaType<Schema>,\n Occ extends TableOccurrence<infer BT, any, any, any>\n ? BT extends BaseTable<any, any, any, any>\n ? BT\n : BaseTable<Schema, any, any, any>\n : BaseTable<Schema, any, any, any>,\n typeof returnPref\n >({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n data: data as Partial<InferSchemaType<Schema>>,\n returnPreference: returnPref as any,\n databaseUseEntityIds: this.database?.isUsingEntityIds() ?? false,\n });\n }\n\n delete(): DeleteBuilder<InferSchemaType<Schema>> {\n return new DeleteBuilder<InferSchemaType<Schema>>({\n occurrence: this.occurrence,\n tableName: this.tableName,\n databaseName: this.databaseName,\n context: this.context,\n databaseUseEntityIds: this.database?.isUsingEntityIds() ?? false,\n });\n }\n\n // Overload for valid relation names - returns typed EntitySet\n navigate<RelationName extends ExtractNavigationNames<Occ>>(\n relationName: RelationName,\n ): EntitySet<\n ExtractSchemaFromOccurrence<\n FindNavigationTarget<Occ, RelationName>\n > extends Record<string, StandardSchemaV1>\n ? ExtractSchemaFromOccurrence<FindNavigationTarget<Occ, RelationName>>\n : Record<string, StandardSchemaV1>,\n FindNavigationTarget<Occ, RelationName>\n >;\n // Overload for arbitrary strings - returns generic EntitySet\n navigate(\n relationName: string,\n ): EntitySet<Record<string, StandardSchemaV1>, undefined>;\n // Implementation\n navigate(relationName: string): EntitySet<any, any> {\n // Use the target occurrence if available, otherwise allow untyped navigation\n // (useful when types might be incomplete)\n const targetOccurrence = this.occurrence?.navigation[relationName];\n const entitySet = new EntitySet<any, any>({\n occurrence: targetOccurrence,\n tableName: targetOccurrence?.name ?? relationName,\n databaseName: this.databaseName,\n context: this.context,\n });\n // Store the navigation info in the EntitySet\n // We'll need to pass this through when creating QueryBuilders\n (entitySet as any).isNavigateFromEntitySet = true;\n (entitySet as any).navigateRelation = relationName;\n (entitySet as any).navigateSourceTableName = this.tableName;\n return entitySet;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { FFetchOptions } from '@fetchkit/ffetch';
2
2
  import { Auth, ExecutionContext, Result } from '../types.js';
3
- import { Database, ValidOccurrenceMix } from './database.js';
3
+ import { Database } from './database.js';
4
4
  import { TableOccurrence } from './table-occurrence.js';
5
5
  export declare class FMServerConnection implements ExecutionContext {
6
6
  private fetchClient;
@@ -24,12 +24,18 @@ export declare class FMServerConnection implements ExecutionContext {
24
24
  _getUseEntityIds(): boolean;
25
25
  /**
26
26
  * @internal
27
+ * Gets the base URL for OData requests
27
28
  */
28
- _makeRequest<T>(url: string, options?: RequestInit & FFetchOptions): Promise<Result<T>>;
29
+ _getBaseUrl(): string;
30
+ /**
31
+ * @internal
32
+ */
33
+ _makeRequest<T>(url: string, options?: RequestInit & FFetchOptions & {
34
+ useEntityIds?: boolean;
35
+ }): Promise<Result<T>>;
29
36
  database<const Occurrences extends readonly TableOccurrence<any, any, any, any>[]>(name: string, config?: {
30
- occurrences?: ValidOccurrenceMix<Occurrences> extends true ? Occurrences : Occurrences & {
31
- __type_error__: "❌ Cannot mix TableOccurrence with and without entity IDs. Either all occurrences must use TableOccurrenceWithIds (with fmtId and fmfIds) or all must be regular TableOccurrence.";
32
- };
37
+ occurrences?: Occurrences | undefined;
38
+ useEntityIds?: boolean;
33
39
  }): Database<Occurrences>;
34
40
  /**
35
41
  * Lists all available databases from the FileMaker OData service.
@@ -2,7 +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 createClient, { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError } from "@fetchkit/ffetch";
5
- import { ODataError, HTTPError } from "../errors.js";
5
+ import { SchemaLockedError, ODataError, HTTPError } from "../errors.js";
6
6
  import { Database } from "./database.js";
7
7
  class FMServerConnection {
8
8
  constructor(config) {
@@ -36,18 +36,26 @@ class FMServerConnection {
36
36
  _getUseEntityIds() {
37
37
  return this.useEntityIds;
38
38
  }
39
+ /**
40
+ * @internal
41
+ * Gets the base URL for OData requests
42
+ */
43
+ _getBaseUrl() {
44
+ return `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
45
+ }
39
46
  /**
40
47
  * @internal
41
48
  */
42
49
  async _makeRequest(url, options) {
43
- var _a, _b;
50
+ var _a, _b, _c, _d, _e, _f;
44
51
  const baseUrl = `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
45
52
  const fullUrl = baseUrl + url;
53
+ const useEntityIds = (options == null ? void 0 : options.useEntityIds) ?? this.useEntityIds;
46
54
  const headers = {
47
55
  Authorization: "apiKey" in this.auth ? `Bearer ${this.auth.apiKey}` : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,
48
56
  "Content-Type": "application/json",
49
57
  Accept: "application/json",
50
- ...this.useEntityIds ? { Prefer: "fmodata.entity-ids" } : {},
58
+ ...useEntityIds ? { Prefer: "fmodata.entity-ids" } : {},
51
59
  ...(options == null ? void 0 : options.headers) || {}
52
60
  };
53
61
  const fetchHandler = options == null ? void 0 : options.fetchHandler;
@@ -58,10 +66,15 @@ class FMServerConnection {
58
66
  } = options || {};
59
67
  const clientToUse = fetchHandler ? createClient({ retries: 0, fetchHandler }) : this.fetchClient;
60
68
  try {
61
- const resp = await clientToUse(fullUrl, {
69
+ const finalOptions = {
62
70
  ...restOptions,
63
71
  headers
64
- });
72
+ };
73
+ const resp = url.includes("/$batch") ? await fetch(fullUrl, {
74
+ method: finalOptions.method,
75
+ headers: finalOptions.headers,
76
+ body: finalOptions.body
77
+ }) : await clientToUse(fullUrl, finalOptions);
65
78
  if (!resp.ok) {
66
79
  let errorBody;
67
80
  try {
@@ -71,12 +84,24 @@ class FMServerConnection {
71
84
  } catch {
72
85
  }
73
86
  if (errorBody == null ? void 0 : errorBody.error) {
87
+ const errorCode = errorBody.error.code;
88
+ const errorMessage = errorBody.error.message || resp.statusText;
89
+ if (errorCode === "303" || errorCode === 303) {
90
+ return {
91
+ data: void 0,
92
+ error: new SchemaLockedError(
93
+ fullUrl,
94
+ errorMessage,
95
+ errorBody.error
96
+ )
97
+ };
98
+ }
74
99
  return {
75
100
  data: void 0,
76
101
  error: new ODataError(
77
102
  fullUrl,
78
- errorBody.error.message || resp.statusText,
79
- errorBody.error.code,
103
+ errorMessage,
104
+ errorCode,
80
105
  errorBody.error
81
106
  )
82
107
  };
@@ -96,19 +121,26 @@ class FMServerConnection {
96
121
  return { data: parseInt(affectedRows, 10), error: void 0 };
97
122
  }
98
123
  if (resp.status === 204) {
124
+ const locationHeader = ((_c = (_b = resp.headers) == null ? void 0 : _b.get) == null ? void 0 : _c.call(_b, "Location")) || ((_e = (_d = resp.headers) == null ? void 0 : _d.get) == null ? void 0 : _e.call(_d, "location"));
125
+ if (locationHeader) {
126
+ return { data: { _location: locationHeader }, error: void 0 };
127
+ }
99
128
  return { data: 0, error: void 0 };
100
129
  }
101
- if ((_b = resp.headers.get("content-type")) == null ? void 0 : _b.includes("application/json")) {
130
+ if ((_f = resp.headers.get("content-type")) == null ? void 0 : _f.includes("application/json")) {
102
131
  const data = await resp.json();
103
132
  if (data.error) {
133
+ const errorCode = data.error.code;
134
+ const errorMessage = data.error.message || "Unknown OData error";
135
+ if (errorCode === "303" || errorCode === 303) {
136
+ return {
137
+ data: void 0,
138
+ error: new SchemaLockedError(fullUrl, errorMessage, data.error)
139
+ };
140
+ }
104
141
  return {
105
142
  data: void 0,
106
- error: new ODataError(
107
- fullUrl,
108
- data.error.message || "Unknown OData error",
109
- data.error.code,
110
- data.error
111
- )
143
+ error: new ODataError(fullUrl, errorMessage, errorCode, data.error)
112
144
  };
113
145
  }
114
146
  return { data, error: void 0 };
@@ -1 +1 @@
1
- {"version":3,"file":"filemaker-odata.js","sources":["../../../src/client/filemaker-odata.ts"],"sourcesContent":["import createClient, {\n FFetchOptions,\n TimeoutError,\n AbortError,\n NetworkError,\n RetryLimitError,\n CircuitOpenError,\n} from \"@fetchkit/ffetch\";\nimport type { Auth, ExecutionContext, Result } from \"../types\";\nimport { HTTPError, ODataError } from \"../errors\";\nimport { Database, type ValidOccurrenceMix } from \"./database\";\nimport { TableOccurrence } from \"./table-occurrence\";\n\nexport class FMServerConnection implements ExecutionContext {\n private fetchClient: ReturnType<typeof createClient>;\n private serverUrl: string;\n private auth: Auth;\n private useEntityIds: boolean = false;\n constructor(config: {\n serverUrl: string;\n auth: Auth;\n fetchClientOptions?: FFetchOptions;\n }) {\n this.fetchClient = createClient({\n retries: 0,\n ...config.fetchClientOptions,\n });\n // Ensure the URL uses https://, is valid, and has no trailing slash\n const url = new URL(config.serverUrl);\n if (url.protocol !== \"https:\") {\n url.protocol = \"https:\";\n }\n // Remove any trailing slash from pathname\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n this.serverUrl = url.toString().replace(/\\/+$/, \"\");\n this.auth = config.auth;\n }\n\n /**\n * @internal\n * Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests\n */\n _setUseEntityIds(useEntityIds: boolean): void {\n this.useEntityIds = useEntityIds;\n }\n\n /**\n * @internal\n * Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests\n */\n _getUseEntityIds(): boolean {\n return this.useEntityIds;\n }\n\n /**\n * @internal\n */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions,\n ): Promise<Result<T>> {\n const baseUrl = `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n const fullUrl = baseUrl + url;\n\n const headers = {\n Authorization:\n \"apiKey\" in this.auth\n ? `Bearer ${this.auth.apiKey}`\n : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(this.useEntityIds ? { Prefer: \"fmodata.entity-ids\" } : {}),\n ...(options?.headers || {}),\n };\n\n // TEMPORARY WORKAROUND: Hopefully this feature will be fixed in the ffetch library\n // Extract fetchHandler and headers separately, only for tests where we're overriding the fetch handler per-request\n const fetchHandler = options?.fetchHandler;\n const {\n headers: _headers,\n fetchHandler: _fetchHandler,\n ...restOptions\n } = options || {};\n\n // If fetchHandler is provided, create a temporary client with it\n // Otherwise use the existing client\n const clientToUse = fetchHandler\n ? createClient({ retries: 0, fetchHandler })\n : this.fetchClient;\n\n try {\n const resp = await clientToUse(fullUrl, {\n ...restOptions,\n headers,\n });\n\n // Handle HTTP errors\n if (!resp.ok) {\n // Try to parse error body if it's JSON\n let errorBody;\n try {\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n errorBody = await resp.json();\n }\n } catch {\n // Ignore JSON parse errors\n }\n\n // Check if it's an OData error response\n if (errorBody?.error) {\n return {\n data: undefined,\n error: new ODataError(\n fullUrl,\n errorBody.error.message || resp.statusText,\n errorBody.error.code,\n errorBody.error,\n ),\n };\n }\n\n return {\n data: undefined,\n error: new HTTPError(\n fullUrl,\n resp.status,\n resp.statusText,\n errorBody,\n ),\n };\n }\n\n // Check for affected rows header (for DELETE and bulk PATCH operations)\n // FileMaker may return this with 204 No Content or 200 OK\n const affectedRows = resp.headers.get(\"fmodata.affected_rows\");\n if (affectedRows !== null) {\n return { data: parseInt(affectedRows, 10) as T, error: undefined };\n }\n\n // Handle 204 No Content with no body\n if (resp.status === 204) {\n return { data: 0 as T, error: undefined };\n }\n\n // Parse response\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n const data = await resp.json();\n\n // Check for embedded OData errors\n if (data.error) {\n return {\n data: undefined,\n error: new ODataError(\n fullUrl,\n data.error.message || \"Unknown OData error\",\n data.error.code,\n data.error,\n ),\n };\n }\n\n return { data: data as T, error: undefined };\n }\n\n return { data: (await resp.text()) as T, error: undefined };\n } catch (err) {\n // Map ffetch errors - return them directly (no re-wrapping)\n if (\n err instanceof TimeoutError ||\n err instanceof AbortError ||\n err instanceof NetworkError ||\n err instanceof RetryLimitError ||\n err instanceof CircuitOpenError\n ) {\n return { data: undefined, error: err };\n }\n\n // Unknown error - wrap it as NetworkError\n return {\n data: undefined,\n error: new NetworkError(fullUrl, err),\n };\n }\n }\n\n database<\n const Occurrences extends readonly TableOccurrence<any, any, any, any>[],\n >(\n name: string,\n config?: {\n occurrences?: ValidOccurrenceMix<Occurrences> extends true\n ? Occurrences\n : Occurrences & {\n __type_error__: \"❌ Cannot mix TableOccurrence with and without entity IDs. Either all occurrences must use TableOccurrenceWithIds (with fmtId and fmfIds) or all must be regular TableOccurrence.\";\n };\n },\n ): Database<Occurrences> {\n return new Database(name, this, config);\n }\n\n /**\n * Lists all available databases from the FileMaker OData service.\n * @returns Promise resolving to an array of database names\n */\n async listDatabaseNames(): Promise<string[]> {\n const result = await this._makeRequest<{\n value?: Array<{ name: string }>;\n }>(\"/\");\n if (result.error) {\n throw result.error;\n }\n if (result.data.value && Array.isArray(result.data.value)) {\n return result.data.value.map((item) => item.name);\n }\n return [];\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"filemaker-odata.js","sources":["../../../src/client/filemaker-odata.ts"],"sourcesContent":["import createClient, {\n FFetchOptions,\n TimeoutError,\n AbortError,\n NetworkError,\n RetryLimitError,\n CircuitOpenError,\n} from \"@fetchkit/ffetch\";\nimport type { Auth, ExecutionContext, Result } from \"../types\";\nimport { HTTPError, ODataError, SchemaLockedError } from \"../errors\";\nimport { Database } from \"./database\";\nimport { TableOccurrence } from \"./table-occurrence\";\n\nexport class FMServerConnection implements ExecutionContext {\n private fetchClient: ReturnType<typeof createClient>;\n private serverUrl: string;\n private auth: Auth;\n private useEntityIds: boolean = false;\n constructor(config: {\n serverUrl: string;\n auth: Auth;\n fetchClientOptions?: FFetchOptions;\n }) {\n this.fetchClient = createClient({\n retries: 0,\n ...config.fetchClientOptions,\n });\n // Ensure the URL uses https://, is valid, and has no trailing slash\n const url = new URL(config.serverUrl);\n if (url.protocol !== \"https:\") {\n url.protocol = \"https:\";\n }\n // Remove any trailing slash from pathname\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n this.serverUrl = url.toString().replace(/\\/+$/, \"\");\n this.auth = config.auth;\n }\n\n /**\n * @internal\n * Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests\n */\n _setUseEntityIds(useEntityIds: boolean): void {\n this.useEntityIds = useEntityIds;\n }\n\n /**\n * @internal\n * Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests\n */\n _getUseEntityIds(): boolean {\n return this.useEntityIds;\n }\n\n /**\n * @internal\n * Gets the base URL for OData requests\n */\n _getBaseUrl(): string {\n return `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n }\n\n /**\n * @internal\n */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions & { useEntityIds?: boolean },\n ): Promise<Result<T>> {\n const baseUrl = `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n const fullUrl = baseUrl + url;\n\n // Use per-request override if provided, otherwise use the database-level setting\n const useEntityIds = options?.useEntityIds ?? this.useEntityIds;\n\n const headers = {\n Authorization:\n \"apiKey\" in this.auth\n ? `Bearer ${this.auth.apiKey}`\n : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(useEntityIds ? { Prefer: \"fmodata.entity-ids\" } : {}),\n ...(options?.headers || {}),\n };\n\n // TEMPORARY WORKAROUND: Hopefully this feature will be fixed in the ffetch library\n // Extract fetchHandler and headers separately, only for tests where we're overriding the fetch handler per-request\n const fetchHandler = options?.fetchHandler;\n const {\n headers: _headers,\n fetchHandler: _fetchHandler,\n ...restOptions\n } = options || {};\n\n // If fetchHandler is provided, create a temporary client with it\n // Otherwise use the existing client\n const clientToUse = fetchHandler\n ? createClient({ retries: 0, fetchHandler })\n : this.fetchClient;\n\n try {\n const finalOptions = {\n ...restOptions,\n headers,\n };\n\n // For batch requests, use native fetch to avoid any potential serialization issues with ffetch\n const resp = url.includes(\"/$batch\")\n ? await fetch(fullUrl, {\n method: finalOptions.method,\n headers: finalOptions.headers,\n body: finalOptions.body,\n })\n : await clientToUse(fullUrl, finalOptions);\n\n // Handle HTTP errors\n if (!resp.ok) {\n // Try to parse error body if it's JSON\n let errorBody;\n try {\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n errorBody = await resp.json();\n }\n } catch {\n // Ignore JSON parse errors\n }\n\n // Check if it's an OData error response\n if (errorBody?.error) {\n const errorCode = errorBody.error.code;\n const errorMessage = errorBody.error.message || resp.statusText;\n\n // Check for schema locked error (code 303)\n if (errorCode === \"303\" || errorCode === 303) {\n return {\n data: undefined,\n error: new SchemaLockedError(\n fullUrl,\n errorMessage,\n errorBody.error,\n ),\n };\n }\n\n return {\n data: undefined,\n error: new ODataError(\n fullUrl,\n errorMessage,\n errorCode,\n errorBody.error,\n ),\n };\n }\n\n return {\n data: undefined,\n error: new HTTPError(\n fullUrl,\n resp.status,\n resp.statusText,\n errorBody,\n ),\n };\n }\n\n // Check for affected rows header (for DELETE and bulk PATCH operations)\n // FileMaker may return this with 204 No Content or 200 OK\n const affectedRows = resp.headers.get(\"fmodata.affected_rows\");\n if (affectedRows !== null) {\n return { data: parseInt(affectedRows, 10) as T, error: undefined };\n }\n\n // Handle 204 No Content with no body\n if (resp.status === 204) {\n // Check for Location header (used for insert with return=minimal)\n // Use optional chaining for safety with mocks that might not have proper headers\n const locationHeader =\n resp.headers?.get?.(\"Location\") || resp.headers?.get?.(\"location\");\n if (locationHeader) {\n // Return the location header so InsertBuilder can extract ROWID\n return { data: { _location: locationHeader } as T, error: undefined };\n }\n return { data: 0 as T, error: undefined };\n }\n\n // Parse response\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n const data = await resp.json();\n\n // Check for embedded OData errors\n if (data.error) {\n const errorCode = data.error.code;\n const errorMessage = data.error.message || \"Unknown OData error\";\n\n // Check for schema locked error (code 303)\n if (errorCode === \"303\" || errorCode === 303) {\n return {\n data: undefined,\n error: new SchemaLockedError(fullUrl, errorMessage, data.error),\n };\n }\n\n return {\n data: undefined,\n error: new ODataError(fullUrl, errorMessage, errorCode, data.error),\n };\n }\n\n return { data: data as T, error: undefined };\n }\n\n return { data: (await resp.text()) as T, error: undefined };\n } catch (err) {\n // Map ffetch errors - return them directly (no re-wrapping)\n if (\n err instanceof TimeoutError ||\n err instanceof AbortError ||\n err instanceof NetworkError ||\n err instanceof RetryLimitError ||\n err instanceof CircuitOpenError\n ) {\n return { data: undefined, error: err };\n }\n\n // Unknown error - wrap it as NetworkError\n return {\n data: undefined,\n error: new NetworkError(fullUrl, err),\n };\n }\n }\n\n database<\n const Occurrences extends readonly TableOccurrence<any, any, any, any>[],\n >(\n name: string,\n config?: {\n occurrences?: Occurrences | undefined;\n useEntityIds?: boolean;\n },\n ): Database<Occurrences> {\n return new Database(name, this, config);\n }\n\n /**\n * Lists all available databases from the FileMaker OData service.\n * @returns Promise resolving to an array of database names\n */\n async listDatabaseNames(): Promise<string[]> {\n const result = await this._makeRequest<{\n value?: Array<{ name: string }>;\n }>(\"/\");\n if (result.error) {\n throw result.error;\n }\n if (result.data.value && Array.isArray(result.data.value)) {\n return result.data.value.map((item) => item.name);\n }\n return [];\n }\n}\n"],"names":[],"mappings":"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"}