@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
@@ -7,18 +7,23 @@ import { InsertBuilder } from "./insert-builder.js";
7
7
  import { DeleteBuilder } from "./delete-builder.js";
8
8
  import { UpdateBuilder } from "./update-builder.js";
9
9
  class EntitySet {
10
+ // Full base path for chained navigations
10
11
  constructor(config) {
11
12
  __publicField(this, "occurrence");
12
13
  __publicField(this, "tableName");
13
14
  __publicField(this, "databaseName");
14
15
  __publicField(this, "context");
16
+ __publicField(this, "database");
17
+ // Database instance for accessing occurrences
15
18
  __publicField(this, "isNavigateFromEntitySet");
16
19
  __publicField(this, "navigateRelation");
17
20
  __publicField(this, "navigateSourceTableName");
21
+ __publicField(this, "navigateBasePath");
18
22
  this.occurrence = config.occurrence;
19
23
  this.tableName = config.tableName;
20
24
  this.databaseName = config.databaseName;
21
25
  this.context = config.context;
26
+ this.database = config.database;
22
27
  }
23
28
  // Type-only method to help TypeScript infer the schema from occurrence
24
29
  static create(config) {
@@ -26,15 +31,18 @@ class EntitySet {
26
31
  occurrence: config.occurrence,
27
32
  tableName: config.tableName,
28
33
  databaseName: config.databaseName,
29
- context: config.context
34
+ context: config.context,
35
+ database: config.database
30
36
  });
31
37
  }
32
38
  list() {
39
+ var _a;
33
40
  const builder = new QueryBuilder({
34
41
  occurrence: this.occurrence,
35
42
  tableName: this.tableName,
36
43
  databaseName: this.databaseName,
37
- context: this.context
44
+ context: this.context,
45
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
38
46
  });
39
47
  if (this.occurrence) {
40
48
  const defaultSelect = this.occurrence.defaultSelect;
@@ -42,28 +50,31 @@ class EntitySet {
42
50
  const schema = this.occurrence.baseTable.schema;
43
51
  const fields = Object.keys(schema);
44
52
  const uniqueFields = [...new Set(fields)];
45
- return builder.select(...uniqueFields);
53
+ return builder.select(...uniqueFields).top(1e3);
46
54
  } else if (Array.isArray(defaultSelect)) {
47
55
  const uniqueFields = [
48
56
  ...new Set(defaultSelect)
49
57
  ];
50
- return builder.select(...uniqueFields);
58
+ return builder.select(...uniqueFields).top(1e3);
51
59
  }
52
60
  }
53
61
  if (this.isNavigateFromEntitySet) {
54
62
  builder.isNavigate = true;
55
63
  builder.navigateRelation = this.navigateRelation;
56
64
  builder.navigateSourceTableName = this.navigateSourceTableName;
65
+ builder.navigateBasePath = this.navigateBasePath;
57
66
  }
58
- return builder;
67
+ return builder.top(1e3);
59
68
  }
60
69
  get(id) {
70
+ var _a;
61
71
  const builder = new RecordBuilder({
62
72
  occurrence: this.occurrence,
63
73
  tableName: this.tableName,
64
74
  databaseName: this.databaseName,
65
75
  context: this.context,
66
- recordId: id
76
+ recordId: id,
77
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
67
78
  });
68
79
  if (this.isNavigateFromEntitySet) {
69
80
  builder.isNavigateFromEntitySet = true;
@@ -72,30 +83,42 @@ class EntitySet {
72
83
  }
73
84
  return builder;
74
85
  }
75
- insert(data) {
86
+ // Implementation
87
+ insert(data, options) {
88
+ var _a;
89
+ const returnPref = (options == null ? void 0 : options.returnFullRecord) === false ? "minimal" : "representation";
76
90
  return new InsertBuilder({
77
91
  occurrence: this.occurrence,
78
92
  tableName: this.tableName,
79
93
  databaseName: this.databaseName,
80
94
  context: this.context,
81
- data
95
+ data,
96
+ returnPreference: returnPref,
97
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
82
98
  });
83
99
  }
84
- update(data) {
100
+ // Implementation
101
+ update(data, options) {
102
+ var _a;
103
+ const returnPref = (options == null ? void 0 : options.returnFullRecord) === true ? "representation" : "minimal";
85
104
  return new UpdateBuilder({
86
105
  occurrence: this.occurrence,
87
106
  tableName: this.tableName,
88
107
  databaseName: this.databaseName,
89
108
  context: this.context,
90
- data
109
+ data,
110
+ returnPreference: returnPref,
111
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
91
112
  });
92
113
  }
93
114
  delete() {
115
+ var _a;
94
116
  return new DeleteBuilder({
95
117
  occurrence: this.occurrence,
96
118
  tableName: this.tableName,
97
119
  databaseName: this.databaseName,
98
- context: this.context
120
+ context: this.context,
121
+ databaseUseEntityIds: ((_a = this.database) == null ? void 0 : _a.isUsingEntityIds()) ?? false
99
122
  });
100
123
  }
101
124
  // Implementation
@@ -110,7 +133,15 @@ class EntitySet {
110
133
  });
111
134
  entitySet.isNavigateFromEntitySet = true;
112
135
  entitySet.navigateRelation = relationName;
113
- entitySet.navigateSourceTableName = this.tableName;
136
+ if (this.isNavigateFromEntitySet && this.navigateBasePath) {
137
+ entitySet.navigateBasePath = `${this.navigateBasePath}/${this.navigateRelation}`;
138
+ entitySet.navigateSourceTableName = this.navigateSourceTableName;
139
+ } else if (this.isNavigateFromEntitySet && this.navigateRelation) {
140
+ entitySet.navigateBasePath = `${this.navigateSourceTableName}/${this.navigateRelation}`;
141
+ entitySet.navigateSourceTableName = this.navigateSourceTableName;
142
+ } else {
143
+ entitySet.navigateSourceTableName = this.tableName;
144
+ }
114
145
  return entitySet;
115
146
  }
116
147
  }
@@ -1 +1 @@
1
- {"version":3,"file":"entity-set.js","sources":["../../../src/client/entity-set.ts"],"sourcesContent":["import { z } from \"zod/v4\";\nimport type {\n ExecutionContext,\n InferSchemaType,\n WithSystemFields,\n InsertData,\n UpdateData,\n} from \"../types\";\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, z.ZodTypeAny>, any>,\n any,\n any,\n any\n >\n : TableOccurrence<\n BaseTable<Record<string, z.ZodTypeAny>, any>,\n any,\n any,\n any\n >\n : TableOccurrence<\n BaseTable<Record<string, z.ZodTypeAny>, 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, z.ZodType>]\n ? InferSchemaType<S>\n : Record<string, any>\n : Record<string, any>\n : Record<string, any>;\n\nexport class EntitySet<\n Schema extends Record<string, z.ZodType> = 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 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 }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n }\n\n // Type-only method to help TypeScript infer the schema from occurrence\n static create<\n OccurrenceSchema extends Record<string, z.ZodType>,\n Occ extends\n | TableOccurrence<BaseTable<OccurrenceSchema, any>, any, any, any>\n | undefined = undefined,\n >(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\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 });\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, z.ZodType>\n ? ExtractSchemaFromOccurrence<FindNavigationTarget<Occ, RelationName>>\n : Record<string, z.ZodTypeAny>,\n FindNavigationTarget<Occ, RelationName>\n >;\n // Overload for arbitrary strings - returns generic EntitySet\n navigate(\n relationName: string,\n ): EntitySet<Record<string, z.ZodTypeAny>, 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":";;;;;;;;AA6FO,MAAM,UAGX;AAAA,EASA,YAAY,QAKT;AAbK;AACA;AACA;AACA;AACA;AACA;AACA;AAQN,SAAK,aAAa,OAAO;AACzB,SAAK,YAAY,OAAO;AACxB,SAAK,eAAe,OAAO;AAC3B,SAAK,UAAU,OAAO;AAAA,EAAA;AAAA;AAAA,EAIxB,OAAO,OAKL,QAKmC;AACnC,WAAO,IAAI,UAAiC;AAAA,MAC1C,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,cAAc,OAAO;AAAA,MACrB,SAAS,OAAO;AAAA,IAAA,CACjB;AAAA,EAAA;AAAA,EAGH,OAQE;AACM,UAAA,UAAU,IAAI,aAQlB;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,IAAA,CACf;AAGD,QAAI,KAAK,YAAY;AACb,YAAA,gBAAgB,KAAK,WAAW;AAEtC,UAAI,kBAAkB,UAAU;AAExB,cAAA,SAAS,KAAK,WAAW,UAAU;AACnC,cAAA,SAAS,OAAO,KAAK,MAAM;AAEjC,cAAM,eAAe,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC;AACjC,eAAA,QAAQ,OAAO,GAAG,YAAY;AAAA,MAC5B,WAAA,MAAM,QAAQ,aAAa,GAAG;AAEvC,cAAM,eAAe;AAAA,UACnB,GAAG,IAAI,IAAI,aAAa;AAAA,QAC1B;AACO,eAAA,QAAQ,OAAO,GAAG,YAAY;AAAA,MAAA;AAAA,IACvC;AAKF,QAAI,KAAK,yBAAyB;AAC/B,cAAgB,aAAa;AAC7B,cAAgB,mBAAmB,KAAK;AACxC,cAAgB,0BAA0B,KAAK;AAAA,IAAA;AAG3C,WAAA;AAAA,EAAA;AAAA,EAGT,IACE,IAMA;AACM,UAAA,UAAU,IAAI,cAKlB;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,UAAU;AAAA,IAAA,CACX;AAED,QAAI,KAAK,yBAAyB;AAC/B,cAAgB,0BAA0B;AAC1C,cAAgB,mBAAmB,KAAK;AACxC,cAAgB,0BAA0B,KAAK;AAAA,IAAA;AAE3C,WAAA;AAAA,EAAA;AAAA,EAGT,OACE,MAK6C;AAC7C,WAAO,IAAI,cAA4C;AAAA,MACrD,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd;AAAA,IAAA,CACD;AAAA,EAAA;AAAA,EAGH,OACE,MAYA;AACA,WAAO,IAAI,cAOT;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd;AAAA,IAAA,CACD;AAAA,EAAA;AAAA,EAGH,SAAiD;AAC/C,WAAO,IAAI,cAAuC;AAAA,MAChD,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,IAAA,CACf;AAAA,EAAA;AAAA;AAAA,EAmBH,SAAS,cAA2C;;AAGlD,UAAM,oBAAmB,UAAK,eAAL,mBAAiB,WAAW;AAC/C,UAAA,YAAY,IAAI,UAAoB;AAAA,MACxC,YAAY;AAAA,MACZ,YAAW,qDAAkB,SAAQ;AAAA,MACrC,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,IAAA,CACf;AAGA,cAAkB,0BAA0B;AAC5C,cAAkB,mBAAmB;AACrC,cAAkB,0BAA0B,KAAK;AAC3C,WAAA;AAAA,EAAA;AAEX;"}
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, any, 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, any, 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 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 ? 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, any, 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 private navigateBasePath?: string; // Full base path for chained navigations\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).top(1000);\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).top(1000);\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 (builder as any).navigateBasePath = this.navigateBasePath;\n // navigateRecordId is intentionally not set (undefined) to indicate navigation from EntitySet\n }\n\n // Apply default pagination limit of 1000 records to prevent stack overflow\n // with large datasets. Users can override with .top() if needed.\n return builder.top(1000);\n }\n\n get(\n id: string | number,\n ): RecordBuilder<\n InferSchemaType<Schema>,\n false,\n keyof InferSchemaType<Schema>,\n Occ,\n keyof InferSchemaType<Schema>,\n {}\n > {\n const builder = new RecordBuilder<\n InferSchemaType<Schema>,\n false,\n keyof InferSchemaType<Schema>,\n Occ,\n keyof InferSchemaType<Schema>,\n {}\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\n // Build the full base path for chained navigations\n // The base path should contain all segments BEFORE the final relation\n if (this.isNavigateFromEntitySet && this.navigateBasePath) {\n // Already have a base path from previous navigation - extend it with current relation\n (entitySet as any).navigateBasePath =\n `${this.navigateBasePath}/${this.navigateRelation}`;\n (entitySet as any).navigateSourceTableName = this.navigateSourceTableName;\n } else if (this.isNavigateFromEntitySet && this.navigateRelation) {\n // First chained navigation - create base path from source/relation\n (entitySet as any).navigateBasePath =\n `${this.navigateSourceTableName}/${this.navigateRelation}`;\n (entitySet as any).navigateSourceTableName = this.navigateSourceTableName;\n } else {\n // Initial navigation - source is just the table name\n (entitySet as any).navigateSourceTableName = this.tableName;\n }\n return entitySet;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { FFetchOptions } from '@fetchkit/ffetch';
2
- import { Auth, ExecutionContext } from '../types.js';
2
+ import { Auth, ExecutionContext, Result } from '../types.js';
3
3
  import { Database } from './database.js';
4
4
  import { TableOccurrence } from './table-occurrence.js';
5
- export declare class FileMakerOData implements ExecutionContext {
5
+ export declare class FMServerConnection implements ExecutionContext {
6
6
  private fetchClient;
7
7
  private serverUrl;
8
8
  private auth;
9
+ private useEntityIds;
9
10
  constructor(config: {
10
11
  serverUrl: string;
11
12
  auth: Auth;
@@ -13,10 +14,28 @@ export declare class FileMakerOData implements ExecutionContext {
13
14
  });
14
15
  /**
15
16
  * @internal
17
+ * Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
16
18
  */
17
- _makeRequest<T>(url: string, options?: RequestInit & FFetchOptions): Promise<T>;
19
+ _setUseEntityIds(useEntityIds: boolean): void;
20
+ /**
21
+ * @internal
22
+ * Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
23
+ */
24
+ _getUseEntityIds(): boolean;
25
+ /**
26
+ * @internal
27
+ * Gets the base URL for OData requests
28
+ */
29
+ _getBaseUrl(): string;
30
+ /**
31
+ * @internal
32
+ */
33
+ _makeRequest<T>(url: string, options?: RequestInit & FFetchOptions & {
34
+ useEntityIds?: boolean;
35
+ }): Promise<Result<T>>;
18
36
  database<const Occurrences extends readonly TableOccurrence<any, any, any, any>[]>(name: string, config?: {
19
- occurrences?: Occurrences;
37
+ occurrences?: Occurrences | undefined;
38
+ useEntityIds?: boolean;
20
39
  }): Database<Occurrences>;
21
40
  /**
22
41
  * Lists all available databases from the FileMaker OData service.
@@ -1,13 +1,15 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import createClient from "@fetchkit/ffetch";
4
+ import createClient, { TimeoutError, AbortError, NetworkError, RetryLimitError, CircuitOpenError } from "@fetchkit/ffetch";
5
+ import { SchemaLockedError, ODataError, HTTPError } from "../errors.js";
5
6
  import { Database } from "./database.js";
6
- class FileMakerOData {
7
+ class FMServerConnection {
7
8
  constructor(config) {
8
9
  __publicField(this, "fetchClient");
9
10
  __publicField(this, "serverUrl");
10
11
  __publicField(this, "auth");
12
+ __publicField(this, "useEntityIds", false);
11
13
  this.fetchClient = createClient({
12
14
  retries: 0,
13
15
  ...config.fetchClientOptions
@@ -20,16 +22,40 @@ class FileMakerOData {
20
22
  this.serverUrl = url.toString().replace(/\/+$/, "");
21
23
  this.auth = config.auth;
22
24
  }
25
+ /**
26
+ * @internal
27
+ * Sets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
28
+ */
29
+ _setUseEntityIds(useEntityIds) {
30
+ this.useEntityIds = useEntityIds;
31
+ }
32
+ /**
33
+ * @internal
34
+ * Gets whether to use FileMaker entity IDs (FMFID/FMTID) in requests
35
+ */
36
+ _getUseEntityIds() {
37
+ return this.useEntityIds;
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
+ }
23
46
  /**
24
47
  * @internal
25
48
  */
26
49
  async _makeRequest(url, options) {
27
- var _a;
50
+ var _a, _b, _c, _d, _e, _f;
28
51
  const baseUrl = `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
52
+ const fullUrl = baseUrl + url;
53
+ const useEntityIds = (options == null ? void 0 : options.useEntityIds) ?? this.useEntityIds;
29
54
  const headers = {
30
55
  Authorization: "apiKey" in this.auth ? `Bearer ${this.auth.apiKey}` : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,
31
56
  "Content-Type": "application/json",
32
57
  Accept: "application/json",
58
+ ...useEntityIds ? { Prefer: "fmodata.entity-ids" } : {},
33
59
  ...(options == null ? void 0 : options.headers) || {}
34
60
  };
35
61
  const fetchHandler = options == null ? void 0 : options.fetchHandler;
@@ -39,30 +65,96 @@ class FileMakerOData {
39
65
  ...restOptions
40
66
  } = options || {};
41
67
  const clientToUse = fetchHandler ? createClient({ retries: 0, fetchHandler }) : this.fetchClient;
42
- const resp = await clientToUse(baseUrl + url, {
43
- ...restOptions,
44
- headers
45
- });
46
- if (!resp.ok) {
47
- throw new Error(
48
- `Failed to make request to ${baseUrl + url}: ${resp.statusText}`
49
- );
50
- }
51
- const affectedRows = resp.headers.get("fmodata.affected_rows");
52
- if (affectedRows !== null) {
53
- return parseInt(affectedRows, 10);
54
- }
55
- if (resp.status === 204) {
56
- return 0;
57
- }
58
- if ((_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
59
- let data = await resp.json();
60
- if (data.error) {
61
- throw new Error(data.error);
68
+ try {
69
+ const finalOptions = {
70
+ ...restOptions,
71
+ headers
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);
78
+ if (!resp.ok) {
79
+ let errorBody;
80
+ try {
81
+ if ((_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
82
+ errorBody = await resp.json();
83
+ }
84
+ } catch {
85
+ }
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
+ }
99
+ return {
100
+ data: void 0,
101
+ error: new ODataError(
102
+ fullUrl,
103
+ errorMessage,
104
+ errorCode,
105
+ errorBody.error
106
+ )
107
+ };
108
+ }
109
+ return {
110
+ data: void 0,
111
+ error: new HTTPError(
112
+ fullUrl,
113
+ resp.status,
114
+ resp.statusText,
115
+ errorBody
116
+ )
117
+ };
62
118
  }
63
- return data;
119
+ const affectedRows = resp.headers.get("fmodata.affected_rows");
120
+ if (affectedRows !== null) {
121
+ return { data: parseInt(affectedRows, 10), error: void 0 };
122
+ }
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
+ }
128
+ return { data: 0, error: void 0 };
129
+ }
130
+ if ((_f = resp.headers.get("content-type")) == null ? void 0 : _f.includes("application/json")) {
131
+ const data = await resp.json();
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
+ }
141
+ return {
142
+ data: void 0,
143
+ error: new ODataError(fullUrl, errorMessage, errorCode, data.error)
144
+ };
145
+ }
146
+ return { data, error: void 0 };
147
+ }
148
+ return { data: await resp.text(), error: void 0 };
149
+ } catch (err) {
150
+ if (err instanceof TimeoutError || err instanceof AbortError || err instanceof NetworkError || err instanceof RetryLimitError || err instanceof CircuitOpenError) {
151
+ return { data: void 0, error: err };
152
+ }
153
+ return {
154
+ data: void 0,
155
+ error: new NetworkError(fullUrl, err)
156
+ };
64
157
  }
65
- return await resp.text();
66
158
  }
67
159
  database(name, config) {
68
160
  return new Database(name, this, config);
@@ -72,14 +164,17 @@ class FileMakerOData {
72
164
  * @returns Promise resolving to an array of database names
73
165
  */
74
166
  async listDatabaseNames() {
75
- const response = await this._makeRequest("/");
76
- if (response.value && Array.isArray(response.value)) {
77
- return response.value.map((item) => item.name);
167
+ const result = await this._makeRequest("/");
168
+ if (result.error) {
169
+ throw result.error;
170
+ }
171
+ if (result.data.value && Array.isArray(result.data.value)) {
172
+ return result.data.value.map((item) => item.name);
78
173
  }
79
174
  return [];
80
175
  }
81
176
  }
82
177
  export {
83
- FileMakerOData
178
+ FMServerConnection
84
179
  };
85
180
  //# sourceMappingURL=filemaker-odata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filemaker-odata.js","sources":["../../../src/client/filemaker-odata.ts"],"sourcesContent":["import createClient, { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { Auth, ExecutionContext } from \"../types\";\nimport { Database } from \"./database\";\nimport { TableOccurrence } from \"./table-occurrence\";\n\nexport class FileMakerOData implements ExecutionContext {\n private fetchClient: ReturnType<typeof createClient>;\n private serverUrl: string;\n private auth: Auth;\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 */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions,\n ): Promise<T> {\n const baseUrl = `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\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 ...(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 const resp = await clientToUse(baseUrl + url, {\n ...restOptions,\n headers,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Failed to make request to ${baseUrl + url}: ${resp.statusText}`,\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 parseInt(affectedRows, 10) as T;\n }\n\n // Handle 204 No Content with no body\n if (resp.status === 204) {\n return 0 as T;\n }\n\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n let data = await resp.json();\n if (data.error) {\n throw new Error(data.error);\n }\n return data as T;\n }\n return (await resp.text()) as T;\n }\n\n database<\n const Occurrences extends readonly TableOccurrence<any, any, any, any>[],\n >(\n name: string,\n config?: { occurrences?: Occurrences },\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 response = (await this._makeRequest(\"/\")) as {\n value?: Array<{ name: string }>;\n };\n if (response.value && Array.isArray(response.value)) {\n return response.value.map((item) => item.name);\n }\n return [];\n }\n}\n"],"names":[],"mappings":";;;;;AAKO,MAAM,eAA2C;AAAA,EAItD,YAAY,QAIT;AAPK;AACA;AACA;AAMN,SAAK,cAAc,aAAa;AAAA,MAC9B,SAAS;AAAA,MACT,GAAG,OAAO;AAAA,IAAA,CACX;AAED,UAAM,MAAM,IAAI,IAAI,OAAO,SAAS;AAChC,QAAA,IAAI,aAAa,UAAU;AAC7B,UAAI,WAAW;AAAA,IAAA;AAGjB,QAAI,WAAW,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAC9C,SAAK,YAAY,IAAI,SAAW,EAAA,QAAQ,QAAQ,EAAE;AAClD,SAAK,OAAO,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMrB,MAAM,aACJ,KACA,SACY;;AACN,UAAA,UAAU,GAAG,KAAK,SAAS,GAAG,YAAY,KAAK,OAAO,UAAU,EAAE;AAExE,UAAM,UAAU;AAAA,MACd,eACE,YAAY,KAAK,OACb,UAAU,KAAK,KAAK,MAAM,KAC1B,SAAS,KAAK,GAAG,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;AAAA,MAClE,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,IAAI,mCAAS,YAAW,CAAA;AAAA,IAC1B;AAIA,UAAM,eAAe,mCAAS;AACxB,UAAA;AAAA,MACJ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,GAAG;AAAA,IACL,IAAI,WAAW,CAAC;AAIV,UAAA,cAAc,eAChB,aAAa,EAAE,SAAS,GAAG,aAAA,CAAc,IACzC,KAAK;AAET,UAAM,OAAO,MAAM,YAAY,UAAU,KAAK;AAAA,MAC5C,GAAG;AAAA,MACH;AAAA,IAAA,CACD;AAEG,QAAA,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI;AAAA,QACR,6BAA6B,UAAU,GAAG,KAAK,KAAK,UAAU;AAAA,MAChE;AAAA,IAAA;AAKF,UAAM,eAAe,KAAK,QAAQ,IAAI,uBAAuB;AAC7D,QAAI,iBAAiB,MAAM;AAClB,aAAA,SAAS,cAAc,EAAE;AAAA,IAAA;AAI9B,QAAA,KAAK,WAAW,KAAK;AAChB,aAAA;AAAA,IAAA;AAGT,SAAI,UAAK,QAAQ,IAAI,cAAc,MAA/B,mBAAkC,SAAS,qBAAqB;AAC9D,UAAA,OAAO,MAAM,KAAK,KAAK;AAC3B,UAAI,KAAK,OAAO;AACR,cAAA,IAAI,MAAM,KAAK,KAAK;AAAA,MAAA;AAErB,aAAA;AAAA,IAAA;AAED,WAAA,MAAM,KAAK,KAAK;AAAA,EAAA;AAAA,EAG1B,SAGE,MACA,QACuB;AACvB,WAAO,IAAI,SAAS,MAAM,MAAM,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxC,MAAM,oBAAuC;AAC3C,UAAM,WAAY,MAAM,KAAK,aAAa,GAAG;AAG7C,QAAI,SAAS,SAAS,MAAM,QAAQ,SAAS,KAAK,GAAG;AACnD,aAAO,SAAS,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,IAAA;AAE/C,WAAO,CAAC;AAAA,EAAA;AAEZ;"}
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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"}
@@ -1,23 +1,58 @@
1
- import { ExecutionContext, ExecutableBuilder, Result, ODataRecordMetadata } from '../types.js';
1
+ import { ExecutionContext, ExecutableBuilder, Result, ExecuteOptions, ConditionallyWithODataAnnotations } from '../types.js';
2
2
  import { TableOccurrence } from './table-occurrence.js';
3
3
  import { FFetchOptions } from '@fetchkit/ffetch';
4
- export declare class InsertBuilder<T extends Record<string, any>, Occ extends TableOccurrence<any, any, any, any> | undefined = undefined> implements ExecutableBuilder<T & ODataRecordMetadata> {
4
+ export type InsertOptions = {
5
+ return?: "minimal" | "representation";
6
+ };
7
+ export declare class InsertBuilder<T extends Record<string, any>, Occ extends TableOccurrence<any, any, any, any> | undefined = undefined, ReturnPreference extends "minimal" | "representation" = "representation"> implements ExecutableBuilder<ReturnPreference extends "minimal" ? {
8
+ ROWID: number;
9
+ } : T> {
5
10
  private occurrence?;
6
11
  private tableName;
7
12
  private databaseName;
8
13
  private context;
9
14
  private data;
15
+ private returnPreference;
16
+ private databaseUseEntityIds;
10
17
  constructor(config: {
11
18
  occurrence?: Occ;
12
19
  tableName: string;
13
20
  databaseName: string;
14
21
  context: ExecutionContext;
15
22
  data: Partial<T>;
23
+ returnPreference?: ReturnPreference;
24
+ databaseUseEntityIds?: boolean;
16
25
  });
17
- execute(options?: RequestInit & FFetchOptions): Promise<Result<T & ODataRecordMetadata>>;
26
+ /**
27
+ * Helper to merge database-level useEntityIds with per-request options
28
+ */
29
+ private mergeExecuteOptions;
30
+ /**
31
+ * Helper to conditionally strip OData annotations based on options
32
+ */
33
+ private stripODataAnnotationsIfNeeded;
34
+ /**
35
+ * Parse ROWID from Location header
36
+ * Expected formats:
37
+ * - contacts(ROWID=4583)
38
+ * - contacts('some-uuid')
39
+ */
40
+ private parseLocationHeader;
41
+ /**
42
+ * Gets the table ID (FMTID) if using entity IDs, otherwise returns the table name
43
+ * @param useEntityIds - Optional override for entity ID usage
44
+ */
45
+ private getTableId;
46
+ execute<EO extends ExecuteOptions>(options?: RequestInit & FFetchOptions & EO): Promise<Result<ReturnPreference extends "minimal" ? {
47
+ ROWID: number;
48
+ } : ConditionallyWithODataAnnotations<T, EO["includeODataAnnotations"] extends true ? true : false>>>;
18
49
  getRequestConfig(): {
19
50
  method: string;
20
51
  url: string;
21
52
  body?: any;
22
53
  };
54
+ toRequest(baseUrl: string): Request;
55
+ processResponse(response: Response, options?: ExecuteOptions): Promise<Result<ReturnPreference extends "minimal" ? {
56
+ ROWID: number;
57
+ } : T>>;
23
58
  }