fms-odata-js 0.3.0 → 0.4.1

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.
@@ -62,7 +62,7 @@ function isFMScriptError(err) {
62
62
  }
63
63
 
64
64
  // src/http.ts
65
- var AUTH_SCHEME_RE = /^(basic|bearer|fmid|negotiate|digest)\s+\S/i;
65
+ var AUTH_SCHEME_RE = /^(basic|bearer|negotiate|digest)\s+\S/i;
66
66
  async function resolveAuthHeader(provider) {
67
67
  const raw = typeof provider === "function" ? await provider() : provider;
68
68
  if (typeof raw !== "string" || raw.length === 0) {
@@ -75,8 +75,8 @@ function basicAuth(user, password) {
75
75
  const b64 = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(unescape(encodeURIComponent(raw)));
76
76
  return `Basic ${b64}`;
77
77
  }
78
- function fmidAuth(token) {
79
- return `FMID ${token}`;
78
+ function bearerAuth(token) {
79
+ return `Bearer ${token}`;
80
80
  }
81
81
  function combineSignals(signals) {
82
82
  const filtered = signals.filter((s) => s !== void 0);
@@ -515,20 +515,21 @@ var Batch = class {
515
515
 
516
516
  // node_modules/@fms-odata/spec-ts/dist/versions.js
517
517
  var FM_VERSION_NAMES = {
518
- "19": "FileMaker 19.x",
519
- "21": "Claris FileMaker 2023",
520
- "22": "Claris FileMaker 2024",
518
+ "20": "Claris FileMaker 2023",
519
+ "21": "Claris FileMaker 2024",
520
+ "22": "Claris FileMaker 2025",
521
521
  "26": "Claris FileMaker 2026",
522
522
  future: "Future / next"
523
523
  };
524
- var ODATA_PROTOCOL_VERSION = "4.0";
524
+ var ODATA_PROTOCOL_VERSION = "4.01";
525
525
  var FM_VERSION_MATRIX = {
526
- "19": {
527
- major: "19",
528
- name: "FileMaker 19.x",
529
- releaseYear: null,
530
- internalVersion: "19.x",
531
- status: "baseline",
526
+ "20": {
527
+ major: "20",
528
+ name: "Claris FileMaker 2023",
529
+ releaseYear: 2023,
530
+ internalVersion: "20.x",
531
+ status: "supported",
532
+ odataProtocolVersion: "4.0",
532
533
  features: {
533
534
  serviceDocument: true,
534
535
  metadata: true,
@@ -550,12 +551,17 @@ var FM_VERSION_MATRIX = {
550
551
  applyAggregation: false,
551
552
  typeCasting: false,
552
553
  parameterizedFilters: false,
554
+ simplifiedQuerySyntax: false,
555
+ nestedQueries: false,
556
+ batchPreferenceInheritance: false,
557
+ metadataFiltering: false,
553
558
  immutableIdUrls: false,
559
+ fmComment: false,
554
560
  aiAnnotation: false,
561
+ computedAnnotation: false,
555
562
  serverVersionAnnotation: false,
556
563
  enrichedFMComment: false,
557
564
  authBasic: true,
558
- authFMID: false,
559
565
  authOAuth: false
560
566
  },
561
567
  queryOptions: {
@@ -573,10 +579,11 @@ var FM_VERSION_MATRIX = {
573
579
  },
574
580
  "21": {
575
581
  major: "21",
576
- name: "Claris FileMaker 2023",
577
- releaseYear: 2023,
582
+ name: "Claris FileMaker 2024",
583
+ releaseYear: 2024,
578
584
  internalVersion: "21.x",
579
585
  status: "supported",
586
+ odataProtocolVersion: "4.01",
580
587
  features: {
581
588
  serviceDocument: true,
582
589
  metadata: true,
@@ -587,23 +594,28 @@ var FM_VERSION_MATRIX = {
587
594
  crossJoin: true,
588
595
  batch: true,
589
596
  scripts: true,
590
- scriptsByFMSID: false,
597
+ scriptsByFMSID: true,
591
598
  scriptListing: false,
592
599
  containerBinaryUpload: true,
593
600
  containerBase64Upload: true,
594
601
  containerDownload: true,
595
602
  schemaModification: true,
596
- webhooks: true,
603
+ webhooks: false,
597
604
  webhookQueryHeaders: false,
598
605
  applyAggregation: false,
599
606
  typeCasting: true,
600
607
  parameterizedFilters: true,
608
+ simplifiedQuerySyntax: true,
609
+ nestedQueries: true,
610
+ batchPreferenceInheritance: true,
611
+ metadataFiltering: false,
601
612
  immutableIdUrls: false,
602
- aiAnnotation: false,
613
+ fmComment: true,
614
+ aiAnnotation: true,
615
+ computedAnnotation: false,
603
616
  serverVersionAnnotation: false,
604
617
  enrichedFMComment: false,
605
618
  authBasic: true,
606
- authFMID: true,
607
619
  authOAuth: true
608
620
  },
609
621
  queryOptions: {
@@ -621,10 +633,11 @@ var FM_VERSION_MATRIX = {
621
633
  },
622
634
  "22": {
623
635
  major: "22",
624
- name: "Claris FileMaker 2024",
625
- releaseYear: 2024,
636
+ name: "Claris FileMaker 2025",
637
+ releaseYear: 2025,
626
638
  internalVersion: "22.x",
627
639
  status: "supported",
640
+ odataProtocolVersion: "4.01",
628
641
  features: {
629
642
  serviceDocument: true,
630
643
  metadata: true,
@@ -635,7 +648,7 @@ var FM_VERSION_MATRIX = {
635
648
  crossJoin: true,
636
649
  batch: true,
637
650
  scripts: true,
638
- scriptsByFMSID: false,
651
+ scriptsByFMSID: true,
639
652
  scriptListing: false,
640
653
  containerBinaryUpload: true,
641
654
  containerBase64Upload: true,
@@ -646,12 +659,17 @@ var FM_VERSION_MATRIX = {
646
659
  applyAggregation: true,
647
660
  typeCasting: true,
648
661
  parameterizedFilters: true,
662
+ simplifiedQuerySyntax: true,
663
+ nestedQueries: true,
664
+ batchPreferenceInheritance: true,
665
+ metadataFiltering: true,
649
666
  immutableIdUrls: false,
650
- aiAnnotation: false,
667
+ fmComment: true,
668
+ aiAnnotation: true,
669
+ computedAnnotation: false,
651
670
  serverVersionAnnotation: false,
652
671
  enrichedFMComment: false,
653
672
  authBasic: true,
654
- authFMID: true,
655
673
  authOAuth: true
656
674
  },
657
675
  queryOptions: {
@@ -673,6 +691,7 @@ var FM_VERSION_MATRIX = {
673
691
  releaseYear: 2026,
674
692
  internalVersion: "26.x",
675
693
  status: "current",
694
+ odataProtocolVersion: "4.01",
676
695
  features: {
677
696
  serviceDocument: true,
678
697
  metadata: true,
@@ -694,12 +713,17 @@ var FM_VERSION_MATRIX = {
694
713
  applyAggregation: true,
695
714
  typeCasting: true,
696
715
  parameterizedFilters: true,
716
+ simplifiedQuerySyntax: true,
717
+ nestedQueries: true,
718
+ batchPreferenceInheritance: true,
719
+ metadataFiltering: true,
697
720
  immutableIdUrls: true,
721
+ fmComment: true,
698
722
  aiAnnotation: true,
723
+ computedAnnotation: true,
699
724
  serverVersionAnnotation: true,
700
725
  enrichedFMComment: true,
701
726
  authBasic: true,
702
- authFMID: true,
703
727
  authOAuth: true
704
728
  },
705
729
  queryOptions: {
@@ -721,6 +745,7 @@ var FM_VERSION_MATRIX = {
721
745
  releaseYear: null,
722
746
  internalVersion: "unknown",
723
747
  status: "future",
748
+ odataProtocolVersion: "4.01",
724
749
  features: {
725
750
  serviceDocument: true,
726
751
  metadata: true,
@@ -742,12 +767,17 @@ var FM_VERSION_MATRIX = {
742
767
  applyAggregation: true,
743
768
  typeCasting: true,
744
769
  parameterizedFilters: true,
770
+ simplifiedQuerySyntax: true,
771
+ nestedQueries: true,
772
+ batchPreferenceInheritance: true,
773
+ metadataFiltering: true,
745
774
  immutableIdUrls: true,
775
+ fmComment: true,
746
776
  aiAnnotation: true,
777
+ computedAnnotation: true,
747
778
  serverVersionAnnotation: true,
748
779
  enrichedFMComment: true,
749
780
  authBasic: true,
750
- authFMID: true,
751
781
  authOAuth: true
752
782
  },
753
783
  queryOptions: {
@@ -771,7 +801,7 @@ function hasQueryOption(version, option) {
771
801
  return FM_VERSION_MATRIX[version]?.queryOptions[option] ?? false;
772
802
  }
773
803
  function minVersionForFeature(feature) {
774
- const order = ["19", "21", "22", "26"];
804
+ const order = ["20", "21", "22", "26"];
775
805
  for (const v of order) {
776
806
  if (FM_VERSION_MATRIX[v].features[feature])
777
807
  return v;
@@ -827,6 +857,43 @@ function parseServerVersion(metadataXml) {
827
857
  return null;
828
858
  }
829
859
 
860
+ // node_modules/@fms-odata/spec-ts/dist/schema.js
861
+ var FIELD_TYPES = [
862
+ "NUMERIC",
863
+ "DECIMAL",
864
+ "INT",
865
+ "DATE",
866
+ "TIME",
867
+ "TIMESTAMP",
868
+ "VARCHAR",
869
+ "CHARACTER VARYING",
870
+ "BLOB",
871
+ "VARBINARY",
872
+ "LONGVARBINARY",
873
+ "BINARY VARYING"
874
+ ];
875
+ var FIELD_DEFAULTS = [
876
+ "USER",
877
+ "USERNAME",
878
+ "CURRENT_USER",
879
+ "CURRENT_DATE",
880
+ "CURDATE",
881
+ "CURRENT_TIME",
882
+ "CURTIME",
883
+ "CURRENT_TIMESTAMP",
884
+ "CURTIMESTAMP"
885
+ ];
886
+ function parseFieldType(typeStr) {
887
+ const lengthMatch = typeStr.match(/\((\d+)\)/);
888
+ const repMatch = typeStr.match(/\[(\d+)\]/);
889
+ const baseType = typeStr.replace(/\(.*?\)/, "").replace(/\[.*?\]/, "").trim();
890
+ return {
891
+ baseType,
892
+ length: lengthMatch ? parseInt(lengthMatch[1], 10) : void 0,
893
+ repetitions: repMatch ? parseInt(repMatch[1], 10) : void 0
894
+ };
895
+ }
896
+
830
897
  // src/metadata.ts
831
898
  function parseBoolAttr(value, defaultValue) {
832
899
  if (value === void 0) return defaultValue;
@@ -1362,17 +1429,19 @@ function parseScriptEnvelope(raw, request) {
1362
1429
  const rawResult = envelope.scriptResult;
1363
1430
  let scriptResult;
1364
1431
  let scriptError;
1365
- if (rawResult !== null && typeof rawResult === "object" && "resultParameter" in rawResult) {
1432
+ let fmsMessage;
1433
+ if (rawResult !== null && typeof rawResult === "object" && ("code" in rawResult || "resultParameter" in rawResult)) {
1366
1434
  const nested = rawResult;
1367
1435
  scriptError = nested.code !== void 0 ? String(nested.code) : "0";
1368
- scriptResult = nested.resultParameter !== void 0 ? String(nested.resultParameter) : void 0;
1436
+ scriptResult = nested.resultParameter !== void 0 ? typeof nested.resultParameter === "string" ? nested.resultParameter : JSON.stringify(nested.resultParameter) : void 0;
1437
+ if (typeof nested.message === "string") fmsMessage = nested.message;
1369
1438
  } else {
1370
1439
  scriptError = envelope.scriptError !== void 0 ? String(envelope.scriptError) : "0";
1371
1440
  scriptResult = rawResult !== void 0 ? typeof rawResult === "string" ? rawResult : JSON.stringify(rawResult) : void 0;
1372
1441
  }
1373
1442
  if (scriptError !== "0") {
1374
1443
  throw new FMScriptError(
1375
- `FileMaker script error ${scriptError}`,
1444
+ fmsMessage ? `FileMaker script error ${scriptError}: ${fmsMessage}` : `FileMaker script error ${scriptError}`,
1376
1445
  {
1377
1446
  status: 200,
1378
1447
  scriptError,
@@ -1759,13 +1828,13 @@ var Query = class _Query {
1759
1828
  return this;
1760
1829
  }
1761
1830
  // -------------------------------------------------------------------------
1762
- // $apply (aggregation) — requires FMS 22.0.1+ (FileMaker 2024)
1831
+ // $apply (aggregation) — requires FMS 22.0.1+ (FileMaker 2025)
1763
1832
  // -------------------------------------------------------------------------
1764
1833
  /**
1765
1834
  * Set a raw `$apply` expression. Use this for advanced transformations
1766
1835
  * that the `aggregate()` / `groupBy()` helpers don't cover.
1767
1836
  *
1768
- * Requires FileMaker Server 2024+ (v22). Use `db.hasFeature('applyAggregation')`
1837
+ * Requires FileMaker Server 2025+ (v22). Use `db.hasFeature('applyAggregation')`
1769
1838
  * to check before calling.
1770
1839
  *
1771
1840
  * @example
@@ -1781,7 +1850,7 @@ var Query = class _Query {
1781
1850
  /**
1782
1851
  * Aggregate the entity set. Produces a `$apply=aggregate(...)` expression.
1783
1852
  *
1784
- * Requires FileMaker Server 2024+ (v22).
1853
+ * Requires FileMaker Server 2025+ (v22).
1785
1854
  *
1786
1855
  * @example
1787
1856
  * ```ts
@@ -1800,7 +1869,7 @@ var Query = class _Query {
1800
1869
  * Group the entity set by one or more fields, optionally with aggregation.
1801
1870
  * Produces a `$apply=groupby((fields), aggregate(...))` expression.
1802
1871
  *
1803
- * Requires FileMaker Server 2024+ (v22).
1872
+ * Requires FileMaker Server 2025+ (v22).
1804
1873
  *
1805
1874
  * @example
1806
1875
  * ```ts
@@ -1951,6 +2020,295 @@ function serializeOptions(s, opts) {
1951
2020
  return pairs.map(([k, v]) => `${k}=${v}`).join(";");
1952
2021
  }
1953
2022
 
2023
+ // src/schema.ts
2024
+ function validateFieldDefinitions(fields) {
2025
+ for (const f of fields) {
2026
+ if (!f.name) throw new TypeError("SchemaEditor: field `name` is required");
2027
+ if (!f.type) throw new TypeError("SchemaEditor: field `type` is required");
2028
+ const { baseType } = parseFieldType(f.type);
2029
+ const upper = baseType.toUpperCase();
2030
+ if (!FIELD_TYPES.includes(upper)) {
2031
+ throw new TypeError(
2032
+ `SchemaEditor: unsupported field type "${f.type}" (base: "${baseType}"). Supported: ${FIELD_TYPES.join(", ")}`
2033
+ );
2034
+ }
2035
+ }
2036
+ }
2037
+ var SchemaEditor = class {
2038
+ constructor(client) {
2039
+ this._client = client;
2040
+ }
2041
+ /**
2042
+ * Create a new table with the given field definitions.
2043
+ *
2044
+ * ```ts
2045
+ * await db.schema().createTable({
2046
+ * tableName: 'Company',
2047
+ * fields: [
2048
+ * { name: 'Company ID', type: 'int', primary: true },
2049
+ * { name: 'Company Name', type: 'varchar(100)', nullable: false },
2050
+ * ],
2051
+ * })
2052
+ * ```
2053
+ */
2054
+ async createTable(params, opts = {}) {
2055
+ if (!params.tableName) throw new TypeError("SchemaEditor: `tableName` is required");
2056
+ if (!params.fields?.length) throw new TypeError("SchemaEditor: `fields` must be a non-empty array");
2057
+ validateFieldDefinitions(params.fields);
2058
+ const url = `${this._client.baseUrl}/FileMaker_Tables`;
2059
+ return executeJson(this._client._ctx, url, {
2060
+ method: "POST",
2061
+ headers: { "Content-Type": "application/json" },
2062
+ body: JSON.stringify(params),
2063
+ accept: "json",
2064
+ ...opts.signal ? { signal: opts.signal } : {}
2065
+ });
2066
+ }
2067
+ /**
2068
+ * Add fields to an existing table.
2069
+ *
2070
+ * ```ts
2071
+ * await db.schema().addFields({
2072
+ * tableName: 'Company',
2073
+ * fields: [{ name: 'Phone', type: 'varchar(30)' }],
2074
+ * })
2075
+ * ```
2076
+ */
2077
+ async addFields(params, opts = {}) {
2078
+ if (!params.tableName) throw new TypeError("SchemaEditor: `tableName` is required");
2079
+ if (!params.fields?.length) throw new TypeError("SchemaEditor: `fields` must be a non-empty array");
2080
+ validateFieldDefinitions(params.fields);
2081
+ const url = `${this._client.baseUrl}/FileMaker_Tables/${encodePathSegment(params.tableName)}`;
2082
+ return executeJson(this._client._ctx, url, {
2083
+ method: "PATCH",
2084
+ headers: { "Content-Type": "application/json" },
2085
+ body: JSON.stringify({ fields: params.fields }),
2086
+ accept: "json",
2087
+ ...opts.signal ? { signal: opts.signal } : {}
2088
+ });
2089
+ }
2090
+ /**
2091
+ * Delete a table and ALL its records. Irreversible.
2092
+ *
2093
+ * Requires `opts.confirm === true` as a safety guard.
2094
+ *
2095
+ * ```ts
2096
+ * await db.schema().deleteTable('OldTable', { confirm: true })
2097
+ * ```
2098
+ */
2099
+ async deleteTable(tableName, opts) {
2100
+ if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
2101
+ if (opts.confirm !== true) {
2102
+ throw new TypeError(
2103
+ `SchemaEditor: deleteTable("${tableName}") requires { confirm: true } \u2014 this operation permanently destroys the table and all its records.`
2104
+ );
2105
+ }
2106
+ const url = `${this._client.baseUrl}/FileMaker_Tables/${encodePathSegment(tableName)}`;
2107
+ await executeJson(this._client._ctx, url, {
2108
+ method: "DELETE",
2109
+ accept: "json",
2110
+ ...opts.signal ? { signal: opts.signal } : {}
2111
+ });
2112
+ }
2113
+ /**
2114
+ * Delete a field from a table. Irreversible.
2115
+ *
2116
+ * Requires `opts.confirm === true` as a safety guard.
2117
+ *
2118
+ * ```ts
2119
+ * await db.schema().deleteField('Company', 'OldField', { confirm: true })
2120
+ * ```
2121
+ */
2122
+ async deleteField(tableName, fieldName, opts) {
2123
+ if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
2124
+ if (!fieldName) throw new TypeError("SchemaEditor: `fieldName` is required");
2125
+ if (opts.confirm !== true) {
2126
+ throw new TypeError(
2127
+ `SchemaEditor: deleteField("${tableName}", "${fieldName}") requires { confirm: true } \u2014 this operation permanently destroys the field and all its data.`
2128
+ );
2129
+ }
2130
+ const url = `${this._client.baseUrl}/FileMaker_Tables/${encodePathSegment(tableName)}/${encodePathSegment(fieldName)}`;
2131
+ await executeJson(this._client._ctx, url, {
2132
+ method: "DELETE",
2133
+ accept: "json",
2134
+ ...opts.signal ? { signal: opts.signal } : {}
2135
+ });
2136
+ }
2137
+ /**
2138
+ * Create an index on a field.
2139
+ *
2140
+ * ```ts
2141
+ * await db.schema().createIndex('Company', 'Company Name')
2142
+ * ```
2143
+ */
2144
+ async createIndex(tableName, fieldName, opts = {}) {
2145
+ if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
2146
+ if (!fieldName) throw new TypeError("SchemaEditor: `fieldName` is required");
2147
+ const url = `${this._client.baseUrl}/FileMaker_Indexes/${encodePathSegment(tableName)}`;
2148
+ return executeJson(this._client._ctx, url, {
2149
+ method: "POST",
2150
+ headers: { "Content-Type": "application/json" },
2151
+ body: JSON.stringify({ indexName: fieldName }),
2152
+ accept: "json",
2153
+ ...opts.signal ? { signal: opts.signal } : {}
2154
+ });
2155
+ }
2156
+ /**
2157
+ * Delete an index from a field.
2158
+ *
2159
+ * ```ts
2160
+ * await db.schema().deleteIndex('Company', 'Company Name')
2161
+ * ```
2162
+ */
2163
+ async deleteIndex(tableName, fieldName, opts = {}) {
2164
+ if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
2165
+ if (!fieldName) throw new TypeError("SchemaEditor: `fieldName` is required");
2166
+ const url = `${this._client.baseUrl}/FileMaker_Indexes/${encodePathSegment(tableName)}/${encodePathSegment(fieldName)}`;
2167
+ await executeJson(this._client._ctx, url, {
2168
+ method: "DELETE",
2169
+ accept: "json",
2170
+ ...opts.signal ? { signal: opts.signal } : {}
2171
+ });
2172
+ }
2173
+ };
2174
+
2175
+ // src/webhooks.ts
2176
+ function normalizeCreateParams(params) {
2177
+ const out = {
2178
+ webhook: params.webhook,
2179
+ tableName: params.tableName
2180
+ };
2181
+ if (params.endpointHeaders) out.endpointHeaders = params.endpointHeaders;
2182
+ if (params.headers && !params.endpointHeaders) out.endpointHeaders = params.headers;
2183
+ if (params.queryHeaders) out.queryHeaders = params.queryHeaders;
2184
+ if (params.notifySchemaChanges !== void 0) out.notifySchemaChanges = params.notifySchemaChanges;
2185
+ if (params.select) out.select = params.select;
2186
+ if (params.filter) out.filter = params.filter;
2187
+ if (params.maxFailedAttempts !== void 0) out.maxFailedAttempts = params.maxFailedAttempts;
2188
+ return out;
2189
+ }
2190
+ function extractWebhookId(data) {
2191
+ if (!data || typeof data !== "object") return void 0;
2192
+ const obj = data;
2193
+ const fromResult = obj.webhookResult;
2194
+ const id = fromResult?.webhookID ?? obj.webhookID ?? obj.id;
2195
+ return id !== void 0 ? String(id) : void 0;
2196
+ }
2197
+ var WebhookManager = class {
2198
+ constructor(client) {
2199
+ this._client = client;
2200
+ }
2201
+ /** Build the URL for a webhook operation. */
2202
+ _url(operation, id) {
2203
+ const base = `${this._client.baseUrl}/Webhook.${operation}`;
2204
+ return id !== void 0 ? `${base}(${encodePathSegment(String(id))})` : base;
2205
+ }
2206
+ /**
2207
+ * Create a webhook.
2208
+ *
2209
+ * ```ts
2210
+ * const { id } = await db.webhooks().create({
2211
+ * webhook: 'https://my.example.com:8080/webhook',
2212
+ * tableName: 'contact',
2213
+ * select: 'id,first_name',
2214
+ * filter: "status eq 'active'",
2215
+ * notifySchemaChanges: true,
2216
+ * })
2217
+ * ```
2218
+ *
2219
+ * @returns An object with the server-generated `id` of the new webhook.
2220
+ */
2221
+ async create(params, opts = {}) {
2222
+ if (!params.webhook) throw new TypeError("WebhookManager: `webhook` URL is required");
2223
+ if (!params.tableName) throw new TypeError("WebhookManager: `tableName` is required");
2224
+ const body = JSON.stringify(normalizeCreateParams(params));
2225
+ const data = await executeJson(this._client._ctx, this._url("Add"), {
2226
+ method: "POST",
2227
+ headers: { "Content-Type": "application/json" },
2228
+ body,
2229
+ accept: "json",
2230
+ ...opts.signal ? { signal: opts.signal } : {}
2231
+ });
2232
+ const id = extractWebhookId(data);
2233
+ if (id === void 0) {
2234
+ throw new Error("WebhookManager: Webhook.Add did not return a webhook id");
2235
+ }
2236
+ return { id };
2237
+ }
2238
+ /**
2239
+ * Delete a webhook by its ID.
2240
+ *
2241
+ * ```ts
2242
+ * await db.webhooks().remove('1')
2243
+ * ```
2244
+ */
2245
+ async remove(id, opts = {}) {
2246
+ if (id === "" || id === void 0) throw new TypeError("WebhookManager: webhook `id` is required");
2247
+ return executeJson(this._client._ctx, this._url("Delete", id), {
2248
+ method: "POST",
2249
+ accept: "json",
2250
+ ...opts.signal ? { signal: opts.signal } : {}
2251
+ });
2252
+ }
2253
+ /** Alias for {@link remove} (matches the FMS endpoint name `Webhook.Delete`). */
2254
+ async delete(id, opts = {}) {
2255
+ return this.remove(id, opts);
2256
+ }
2257
+ /**
2258
+ * Get a specific webhook's data by ID.
2259
+ *
2260
+ * ```ts
2261
+ * const data = await db.webhooks().get('1')
2262
+ * ```
2263
+ */
2264
+ async get(id, opts = {}) {
2265
+ if (id === "" || id === void 0) throw new TypeError("WebhookManager: webhook `id` is required");
2266
+ return executeJson(this._client._ctx, this._url("Get", id), {
2267
+ method: "GET",
2268
+ accept: "json",
2269
+ ...opts.signal ? { signal: opts.signal } : {}
2270
+ });
2271
+ }
2272
+ /**
2273
+ * List all webhooks.
2274
+ *
2275
+ * ```ts
2276
+ * const result = await db.webhooks().getAll()
2277
+ * ```
2278
+ */
2279
+ async getAll(opts = {}) {
2280
+ return executeJson(this._client._ctx, this._url("GetAll"), {
2281
+ method: "GET",
2282
+ accept: "json",
2283
+ ...opts.signal ? { signal: opts.signal } : {}
2284
+ });
2285
+ }
2286
+ /**
2287
+ * Manually invoke (trigger) a webhook by ID. Useful for testing.
2288
+ *
2289
+ * Optionally pass `rowIDs` to target specific records; if omitted, FMS
2290
+ * triggers the webhook for all pending records.
2291
+ *
2292
+ * ```ts
2293
+ * await db.webhooks().invoke('1')
2294
+ * await db.webhooks().invoke('1', { rowIDs: [10, 20] })
2295
+ * ```
2296
+ */
2297
+ async invoke(id, opts = {}) {
2298
+ if (id === "" || id === void 0) throw new TypeError("WebhookManager: webhook `id` is required");
2299
+ const { rowIDs, signal, ...rest } = opts;
2300
+ const body = JSON.stringify({ rowIDs: rowIDs ? [...rowIDs] : [] });
2301
+ return executeJson(this._client._ctx, this._url("Invoke", id), {
2302
+ method: "POST",
2303
+ headers: { "Content-Type": "application/json" },
2304
+ body,
2305
+ accept: "json",
2306
+ ...signal ? { signal } : {},
2307
+ ...rest
2308
+ });
2309
+ }
2310
+ };
2311
+
1954
2312
  // src/client.ts
1955
2313
  var FMSOData = class {
1956
2314
  constructor(options) {
@@ -2054,7 +2412,7 @@ var FMSOData = class {
2054
2412
  * `@fms-odata/spec-ts` `parseServerVersion`). The result is cached for the
2055
2413
  * lifetime of this `FMSOData` instance.
2056
2414
  *
2057
- * Returns the major version string (`'19'`, `'21'`, `'22'`, `'26'`) or
2415
+ * Returns the major version string (`'20'`, `'21'`, `'22'`, `'26'`) or
2058
2416
  * `'future'` if the version is newer than the spec knows about. Returns
2059
2417
  * `null` if the version cannot be determined (e.g. the metadata lacks the
2060
2418
  * annotation).
@@ -2138,6 +2496,75 @@ var FMSOData = class {
2138
2496
  if (!v) return false;
2139
2497
  return hasFeature(v, feature);
2140
2498
  }
2499
+ /**
2500
+ * Get a `SchemaEditor` handle for DDL operations (create/delete tables,
2501
+ * add/delete fields, create/delete indexes). Requires a FileMaker account
2502
+ * with full access privileges.
2503
+ *
2504
+ * ```ts
2505
+ * await db.schema().createTable({ tableName: 'Company', fields: [...] })
2506
+ * ```
2507
+ */
2508
+ schema() {
2509
+ if (!this._schemaEditor) this._schemaEditor = new SchemaEditor(this);
2510
+ return this._schemaEditor;
2511
+ }
2512
+ /** Convenience: create a table. See {@link SchemaEditor#createTable}. */
2513
+ async createTable(params, opts = {}) {
2514
+ return this.schema().createTable(params, opts);
2515
+ }
2516
+ /** Convenience: add fields to a table. See {@link SchemaEditor#addFields}. */
2517
+ async addFields(params, opts = {}) {
2518
+ return this.schema().addFields(params, opts);
2519
+ }
2520
+ /** Convenience: delete a table (requires `confirm: true`). See {@link SchemaEditor#deleteTable}. */
2521
+ async deleteTable(tableName, opts) {
2522
+ return this.schema().deleteTable(tableName, opts);
2523
+ }
2524
+ /** Convenience: delete a field (requires `confirm: true`). See {@link SchemaEditor#deleteField}. */
2525
+ async deleteField(tableName, fieldName, opts) {
2526
+ return this.schema().deleteField(tableName, fieldName, opts);
2527
+ }
2528
+ /** Convenience: create an index. See {@link SchemaEditor#createIndex}. */
2529
+ async createIndex(tableName, fieldName, opts = {}) {
2530
+ return this.schema().createIndex(tableName, fieldName, opts);
2531
+ }
2532
+ /** Convenience: delete an index. See {@link SchemaEditor#deleteIndex}. */
2533
+ async deleteIndex(tableName, fieldName, opts = {}) {
2534
+ return this.schema().deleteIndex(tableName, fieldName, opts);
2535
+ }
2536
+ /**
2537
+ * Get a `WebhookManager` handle for webhook CRUD operations (create, remove,
2538
+ * get, getAll, invoke). Requires FileMaker Server 2025+ (v22).
2539
+ *
2540
+ * ```ts
2541
+ * await db.webhooks().create({ webhook: 'https://...', tableName: 'contact' })
2542
+ * ```
2543
+ */
2544
+ webhooks() {
2545
+ if (!this._webhookManager) this._webhookManager = new WebhookManager(this);
2546
+ return this._webhookManager;
2547
+ }
2548
+ /** Convenience: create a webhook. See {@link WebhookManager#create}. */
2549
+ async createWebhook(params, opts = {}) {
2550
+ return this.webhooks().create(params, opts);
2551
+ }
2552
+ /** Convenience: delete a webhook by ID. See {@link WebhookManager#remove}. */
2553
+ async removeWebhook(id, opts = {}) {
2554
+ return this.webhooks().remove(id, opts);
2555
+ }
2556
+ /** Convenience: get a webhook by ID. See {@link WebhookManager#get}. */
2557
+ async getWebhook(id, opts = {}) {
2558
+ return this.webhooks().get(id, opts);
2559
+ }
2560
+ /** Convenience: list all webhooks. See {@link WebhookManager#getAll}. */
2561
+ async getAllWebhooks(opts = {}) {
2562
+ return this.webhooks().getAll(opts);
2563
+ }
2564
+ /** Convenience: manually invoke a webhook by ID. See {@link WebhookManager#invoke}. */
2565
+ async invokeWebhook(id, opts = {}) {
2566
+ return this.webhooks().invoke(id, opts);
2567
+ }
2141
2568
  /**
2142
2569
  * Create a new `$batch` builder for composing multiple OData operations
2143
2570
  * into a single HTTP round-trip.
@@ -2171,6 +2598,8 @@ export {
2171
2598
  Changeset,
2172
2599
  ContainerRef,
2173
2600
  EntityRef,
2601
+ FIELD_DEFAULTS,
2602
+ FIELD_TYPES,
2174
2603
  FMSOData as FMOData,
2175
2604
  FMSODataError as FMODataError,
2176
2605
  FMSOData,
@@ -2183,17 +2612,20 @@ export {
2183
2612
  MetadataFetcher,
2184
2613
  ODATA_PROTOCOL_VERSION,
2185
2614
  Query,
2615
+ SchemaEditor,
2186
2616
  ScriptInvoker,
2617
+ WebhookManager,
2187
2618
  basicAuth,
2619
+ bearerAuth,
2188
2620
  buildContainerJsonBody,
2189
2621
  filterFactory,
2190
- fmidAuth,
2191
2622
  hasFeature,
2192
2623
  hasQueryOption,
2193
2624
  isFMSODataError as isFMODataError,
2194
2625
  isFMSODataError,
2195
2626
  isFMScriptError,
2196
2627
  minVersionForFeature,
2628
+ parseFieldType,
2197
2629
  parseServerVersion,
2198
2630
  parseVersionString,
2199
2631
  sniffContainerMime,