fms-odata-js 0.3.0 → 0.4.0
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.
- package/CHANGELOG.md +75 -1
- package/README.md +94 -21
- package/dist/client.d.ts +46 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/fms-odata.esm.js +464 -34
- package/dist/fms-odata.esm.min.js +4 -4
- package/dist/fms-odata.iife.min.js +3 -3
- package/dist/http.d.ts +3 -3
- package/dist/http.d.ts.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/query.d.ts +3 -3
- package/dist/schema.d.ts +103 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/webhooks.d.ts +98 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/client.ts +118 -1
- package/src/http.ts +6 -6
- package/src/index.ts +18 -1
- package/src/query.ts +4 -4
- package/src/schema.ts +236 -0
- package/src/webhooks.ts +203 -0
package/dist/fms-odata.esm.js
CHANGED
|
@@ -62,7 +62,7 @@ function isFMScriptError(err) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// src/http.ts
|
|
65
|
-
var AUTH_SCHEME_RE = /^(basic|bearer|
|
|
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
|
|
79
|
-
return `
|
|
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
|
-
"
|
|
519
|
-
"21": "Claris FileMaker
|
|
520
|
-
"22": "Claris FileMaker
|
|
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.
|
|
524
|
+
var ODATA_PROTOCOL_VERSION = "4.01";
|
|
525
525
|
var FM_VERSION_MATRIX = {
|
|
526
|
-
"
|
|
527
|
-
major: "
|
|
528
|
-
name: "FileMaker
|
|
529
|
-
releaseYear:
|
|
530
|
-
internalVersion: "
|
|
531
|
-
status: "
|
|
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
|
|
577
|
-
releaseYear:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
625
|
-
releaseYear:
|
|
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:
|
|
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
|
-
|
|
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 = ["
|
|
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;
|
|
@@ -1759,13 +1826,13 @@ var Query = class _Query {
|
|
|
1759
1826
|
return this;
|
|
1760
1827
|
}
|
|
1761
1828
|
// -------------------------------------------------------------------------
|
|
1762
|
-
// $apply (aggregation) — requires FMS 22.0.1+ (FileMaker
|
|
1829
|
+
// $apply (aggregation) — requires FMS 22.0.1+ (FileMaker 2025)
|
|
1763
1830
|
// -------------------------------------------------------------------------
|
|
1764
1831
|
/**
|
|
1765
1832
|
* Set a raw `$apply` expression. Use this for advanced transformations
|
|
1766
1833
|
* that the `aggregate()` / `groupBy()` helpers don't cover.
|
|
1767
1834
|
*
|
|
1768
|
-
* Requires FileMaker Server
|
|
1835
|
+
* Requires FileMaker Server 2025+ (v22). Use `db.hasFeature('applyAggregation')`
|
|
1769
1836
|
* to check before calling.
|
|
1770
1837
|
*
|
|
1771
1838
|
* @example
|
|
@@ -1781,7 +1848,7 @@ var Query = class _Query {
|
|
|
1781
1848
|
/**
|
|
1782
1849
|
* Aggregate the entity set. Produces a `$apply=aggregate(...)` expression.
|
|
1783
1850
|
*
|
|
1784
|
-
* Requires FileMaker Server
|
|
1851
|
+
* Requires FileMaker Server 2025+ (v22).
|
|
1785
1852
|
*
|
|
1786
1853
|
* @example
|
|
1787
1854
|
* ```ts
|
|
@@ -1800,7 +1867,7 @@ var Query = class _Query {
|
|
|
1800
1867
|
* Group the entity set by one or more fields, optionally with aggregation.
|
|
1801
1868
|
* Produces a `$apply=groupby((fields), aggregate(...))` expression.
|
|
1802
1869
|
*
|
|
1803
|
-
* Requires FileMaker Server
|
|
1870
|
+
* Requires FileMaker Server 2025+ (v22).
|
|
1804
1871
|
*
|
|
1805
1872
|
* @example
|
|
1806
1873
|
* ```ts
|
|
@@ -1951,6 +2018,295 @@ function serializeOptions(s, opts) {
|
|
|
1951
2018
|
return pairs.map(([k, v]) => `${k}=${v}`).join(";");
|
|
1952
2019
|
}
|
|
1953
2020
|
|
|
2021
|
+
// src/schema.ts
|
|
2022
|
+
function validateFieldDefinitions(fields) {
|
|
2023
|
+
for (const f of fields) {
|
|
2024
|
+
if (!f.name) throw new TypeError("SchemaEditor: field `name` is required");
|
|
2025
|
+
if (!f.type) throw new TypeError("SchemaEditor: field `type` is required");
|
|
2026
|
+
const { baseType } = parseFieldType(f.type);
|
|
2027
|
+
const upper = baseType.toUpperCase();
|
|
2028
|
+
if (!FIELD_TYPES.includes(upper)) {
|
|
2029
|
+
throw new TypeError(
|
|
2030
|
+
`SchemaEditor: unsupported field type "${f.type}" (base: "${baseType}"). Supported: ${FIELD_TYPES.join(", ")}`
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
var SchemaEditor = class {
|
|
2036
|
+
constructor(client) {
|
|
2037
|
+
this._client = client;
|
|
2038
|
+
}
|
|
2039
|
+
/**
|
|
2040
|
+
* Create a new table with the given field definitions.
|
|
2041
|
+
*
|
|
2042
|
+
* ```ts
|
|
2043
|
+
* await db.schema().createTable({
|
|
2044
|
+
* tableName: 'Company',
|
|
2045
|
+
* fields: [
|
|
2046
|
+
* { name: 'Company ID', type: 'int', primary: true },
|
|
2047
|
+
* { name: 'Company Name', type: 'varchar(100)', nullable: false },
|
|
2048
|
+
* ],
|
|
2049
|
+
* })
|
|
2050
|
+
* ```
|
|
2051
|
+
*/
|
|
2052
|
+
async createTable(params, opts = {}) {
|
|
2053
|
+
if (!params.tableName) throw new TypeError("SchemaEditor: `tableName` is required");
|
|
2054
|
+
if (!params.fields?.length) throw new TypeError("SchemaEditor: `fields` must be a non-empty array");
|
|
2055
|
+
validateFieldDefinitions(params.fields);
|
|
2056
|
+
const url = `${this._client.baseUrl}/FileMaker_Tables`;
|
|
2057
|
+
return executeJson(this._client._ctx, url, {
|
|
2058
|
+
method: "POST",
|
|
2059
|
+
headers: { "Content-Type": "application/json" },
|
|
2060
|
+
body: JSON.stringify(params),
|
|
2061
|
+
accept: "json",
|
|
2062
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Add fields to an existing table.
|
|
2067
|
+
*
|
|
2068
|
+
* ```ts
|
|
2069
|
+
* await db.schema().addFields({
|
|
2070
|
+
* tableName: 'Company',
|
|
2071
|
+
* fields: [{ name: 'Phone', type: 'varchar(30)' }],
|
|
2072
|
+
* })
|
|
2073
|
+
* ```
|
|
2074
|
+
*/
|
|
2075
|
+
async addFields(params, opts = {}) {
|
|
2076
|
+
if (!params.tableName) throw new TypeError("SchemaEditor: `tableName` is required");
|
|
2077
|
+
if (!params.fields?.length) throw new TypeError("SchemaEditor: `fields` must be a non-empty array");
|
|
2078
|
+
validateFieldDefinitions(params.fields);
|
|
2079
|
+
const url = `${this._client.baseUrl}/FileMaker_Tables/${encodePathSegment(params.tableName)}`;
|
|
2080
|
+
return executeJson(this._client._ctx, url, {
|
|
2081
|
+
method: "PATCH",
|
|
2082
|
+
headers: { "Content-Type": "application/json" },
|
|
2083
|
+
body: JSON.stringify({ fields: params.fields }),
|
|
2084
|
+
accept: "json",
|
|
2085
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Delete a table and ALL its records. Irreversible.
|
|
2090
|
+
*
|
|
2091
|
+
* Requires `opts.confirm === true` as a safety guard.
|
|
2092
|
+
*
|
|
2093
|
+
* ```ts
|
|
2094
|
+
* await db.schema().deleteTable('OldTable', { confirm: true })
|
|
2095
|
+
* ```
|
|
2096
|
+
*/
|
|
2097
|
+
async deleteTable(tableName, opts) {
|
|
2098
|
+
if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
|
|
2099
|
+
if (opts.confirm !== true) {
|
|
2100
|
+
throw new TypeError(
|
|
2101
|
+
`SchemaEditor: deleteTable("${tableName}") requires { confirm: true } \u2014 this operation permanently destroys the table and all its records.`
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
2104
|
+
const url = `${this._client.baseUrl}/FileMaker_Tables/${encodePathSegment(tableName)}`;
|
|
2105
|
+
await executeJson(this._client._ctx, url, {
|
|
2106
|
+
method: "DELETE",
|
|
2107
|
+
accept: "json",
|
|
2108
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
/**
|
|
2112
|
+
* Delete a field from a table. Irreversible.
|
|
2113
|
+
*
|
|
2114
|
+
* Requires `opts.confirm === true` as a safety guard.
|
|
2115
|
+
*
|
|
2116
|
+
* ```ts
|
|
2117
|
+
* await db.schema().deleteField('Company', 'OldField', { confirm: true })
|
|
2118
|
+
* ```
|
|
2119
|
+
*/
|
|
2120
|
+
async deleteField(tableName, fieldName, opts) {
|
|
2121
|
+
if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
|
|
2122
|
+
if (!fieldName) throw new TypeError("SchemaEditor: `fieldName` is required");
|
|
2123
|
+
if (opts.confirm !== true) {
|
|
2124
|
+
throw new TypeError(
|
|
2125
|
+
`SchemaEditor: deleteField("${tableName}", "${fieldName}") requires { confirm: true } \u2014 this operation permanently destroys the field and all its data.`
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
const url = `${this._client.baseUrl}/FileMaker_Tables/${encodePathSegment(tableName)}/${encodePathSegment(fieldName)}`;
|
|
2129
|
+
await executeJson(this._client._ctx, url, {
|
|
2130
|
+
method: "DELETE",
|
|
2131
|
+
accept: "json",
|
|
2132
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
/**
|
|
2136
|
+
* Create an index on a field.
|
|
2137
|
+
*
|
|
2138
|
+
* ```ts
|
|
2139
|
+
* await db.schema().createIndex('Company', 'Company Name')
|
|
2140
|
+
* ```
|
|
2141
|
+
*/
|
|
2142
|
+
async createIndex(tableName, fieldName, opts = {}) {
|
|
2143
|
+
if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
|
|
2144
|
+
if (!fieldName) throw new TypeError("SchemaEditor: `fieldName` is required");
|
|
2145
|
+
const url = `${this._client.baseUrl}/FileMaker_Indexes/${encodePathSegment(tableName)}`;
|
|
2146
|
+
return executeJson(this._client._ctx, url, {
|
|
2147
|
+
method: "POST",
|
|
2148
|
+
headers: { "Content-Type": "application/json" },
|
|
2149
|
+
body: JSON.stringify({ indexName: fieldName }),
|
|
2150
|
+
accept: "json",
|
|
2151
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
/**
|
|
2155
|
+
* Delete an index from a field.
|
|
2156
|
+
*
|
|
2157
|
+
* ```ts
|
|
2158
|
+
* await db.schema().deleteIndex('Company', 'Company Name')
|
|
2159
|
+
* ```
|
|
2160
|
+
*/
|
|
2161
|
+
async deleteIndex(tableName, fieldName, opts = {}) {
|
|
2162
|
+
if (!tableName) throw new TypeError("SchemaEditor: `tableName` is required");
|
|
2163
|
+
if (!fieldName) throw new TypeError("SchemaEditor: `fieldName` is required");
|
|
2164
|
+
const url = `${this._client.baseUrl}/FileMaker_Indexes/${encodePathSegment(tableName)}/${encodePathSegment(fieldName)}`;
|
|
2165
|
+
await executeJson(this._client._ctx, url, {
|
|
2166
|
+
method: "DELETE",
|
|
2167
|
+
accept: "json",
|
|
2168
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
|
|
2173
|
+
// src/webhooks.ts
|
|
2174
|
+
function normalizeCreateParams(params) {
|
|
2175
|
+
const out = {
|
|
2176
|
+
webhook: params.webhook,
|
|
2177
|
+
tableName: params.tableName
|
|
2178
|
+
};
|
|
2179
|
+
if (params.endpointHeaders) out.endpointHeaders = params.endpointHeaders;
|
|
2180
|
+
if (params.headers && !params.endpointHeaders) out.endpointHeaders = params.headers;
|
|
2181
|
+
if (params.queryHeaders) out.queryHeaders = params.queryHeaders;
|
|
2182
|
+
if (params.notifySchemaChanges !== void 0) out.notifySchemaChanges = params.notifySchemaChanges;
|
|
2183
|
+
if (params.select) out.select = params.select;
|
|
2184
|
+
if (params.filter) out.filter = params.filter;
|
|
2185
|
+
if (params.maxFailedAttempts !== void 0) out.maxFailedAttempts = params.maxFailedAttempts;
|
|
2186
|
+
return out;
|
|
2187
|
+
}
|
|
2188
|
+
function extractWebhookId(data) {
|
|
2189
|
+
if (!data || typeof data !== "object") return void 0;
|
|
2190
|
+
const obj = data;
|
|
2191
|
+
const fromResult = obj.webhookResult;
|
|
2192
|
+
const id = fromResult?.webhookID ?? obj.webhookID ?? obj.id;
|
|
2193
|
+
return id !== void 0 ? String(id) : void 0;
|
|
2194
|
+
}
|
|
2195
|
+
var WebhookManager = class {
|
|
2196
|
+
constructor(client) {
|
|
2197
|
+
this._client = client;
|
|
2198
|
+
}
|
|
2199
|
+
/** Build the URL for a webhook operation. */
|
|
2200
|
+
_url(operation, id) {
|
|
2201
|
+
const base = `${this._client.baseUrl}/Webhook.${operation}`;
|
|
2202
|
+
return id !== void 0 ? `${base}(${encodePathSegment(String(id))})` : base;
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Create a webhook.
|
|
2206
|
+
*
|
|
2207
|
+
* ```ts
|
|
2208
|
+
* const { id } = await db.webhooks().create({
|
|
2209
|
+
* webhook: 'https://my.example.com:8080/webhook',
|
|
2210
|
+
* tableName: 'contact',
|
|
2211
|
+
* select: 'id,first_name',
|
|
2212
|
+
* filter: "status eq 'active'",
|
|
2213
|
+
* notifySchemaChanges: true,
|
|
2214
|
+
* })
|
|
2215
|
+
* ```
|
|
2216
|
+
*
|
|
2217
|
+
* @returns An object with the server-generated `id` of the new webhook.
|
|
2218
|
+
*/
|
|
2219
|
+
async create(params, opts = {}) {
|
|
2220
|
+
if (!params.webhook) throw new TypeError("WebhookManager: `webhook` URL is required");
|
|
2221
|
+
if (!params.tableName) throw new TypeError("WebhookManager: `tableName` is required");
|
|
2222
|
+
const body = JSON.stringify(normalizeCreateParams(params));
|
|
2223
|
+
const data = await executeJson(this._client._ctx, this._url("Add"), {
|
|
2224
|
+
method: "POST",
|
|
2225
|
+
headers: { "Content-Type": "application/json" },
|
|
2226
|
+
body,
|
|
2227
|
+
accept: "json",
|
|
2228
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2229
|
+
});
|
|
2230
|
+
const id = extractWebhookId(data);
|
|
2231
|
+
if (id === void 0) {
|
|
2232
|
+
throw new Error("WebhookManager: Webhook.Add did not return a webhook id");
|
|
2233
|
+
}
|
|
2234
|
+
return { id };
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2237
|
+
* Delete a webhook by its ID.
|
|
2238
|
+
*
|
|
2239
|
+
* ```ts
|
|
2240
|
+
* await db.webhooks().remove('1')
|
|
2241
|
+
* ```
|
|
2242
|
+
*/
|
|
2243
|
+
async remove(id, opts = {}) {
|
|
2244
|
+
if (id === "" || id === void 0) throw new TypeError("WebhookManager: webhook `id` is required");
|
|
2245
|
+
return executeJson(this._client._ctx, this._url("Delete", id), {
|
|
2246
|
+
method: "POST",
|
|
2247
|
+
accept: "json",
|
|
2248
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
/** Alias for {@link remove} (matches the FMS endpoint name `Webhook.Delete`). */
|
|
2252
|
+
async delete(id, opts = {}) {
|
|
2253
|
+
return this.remove(id, opts);
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Get a specific webhook's data by ID.
|
|
2257
|
+
*
|
|
2258
|
+
* ```ts
|
|
2259
|
+
* const data = await db.webhooks().get('1')
|
|
2260
|
+
* ```
|
|
2261
|
+
*/
|
|
2262
|
+
async get(id, opts = {}) {
|
|
2263
|
+
if (id === "" || id === void 0) throw new TypeError("WebhookManager: webhook `id` is required");
|
|
2264
|
+
return executeJson(this._client._ctx, this._url("Get", id), {
|
|
2265
|
+
method: "GET",
|
|
2266
|
+
accept: "json",
|
|
2267
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
2271
|
+
* List all webhooks.
|
|
2272
|
+
*
|
|
2273
|
+
* ```ts
|
|
2274
|
+
* const result = await db.webhooks().getAll()
|
|
2275
|
+
* ```
|
|
2276
|
+
*/
|
|
2277
|
+
async getAll(opts = {}) {
|
|
2278
|
+
return executeJson(this._client._ctx, this._url("GetAll"), {
|
|
2279
|
+
method: "GET",
|
|
2280
|
+
accept: "json",
|
|
2281
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Manually invoke (trigger) a webhook by ID. Useful for testing.
|
|
2286
|
+
*
|
|
2287
|
+
* Optionally pass `rowIDs` to target specific records; if omitted, FMS
|
|
2288
|
+
* triggers the webhook for all pending records.
|
|
2289
|
+
*
|
|
2290
|
+
* ```ts
|
|
2291
|
+
* await db.webhooks().invoke('1')
|
|
2292
|
+
* await db.webhooks().invoke('1', { rowIDs: [10, 20] })
|
|
2293
|
+
* ```
|
|
2294
|
+
*/
|
|
2295
|
+
async invoke(id, opts = {}) {
|
|
2296
|
+
if (id === "" || id === void 0) throw new TypeError("WebhookManager: webhook `id` is required");
|
|
2297
|
+
const { rowIDs, signal, ...rest } = opts;
|
|
2298
|
+
const body = JSON.stringify({ rowIDs: rowIDs ? [...rowIDs] : [] });
|
|
2299
|
+
return executeJson(this._client._ctx, this._url("Invoke", id), {
|
|
2300
|
+
method: "POST",
|
|
2301
|
+
headers: { "Content-Type": "application/json" },
|
|
2302
|
+
body,
|
|
2303
|
+
accept: "json",
|
|
2304
|
+
...signal ? { signal } : {},
|
|
2305
|
+
...rest
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
|
|
1954
2310
|
// src/client.ts
|
|
1955
2311
|
var FMSOData = class {
|
|
1956
2312
|
constructor(options) {
|
|
@@ -2054,7 +2410,7 @@ var FMSOData = class {
|
|
|
2054
2410
|
* `@fms-odata/spec-ts` `parseServerVersion`). The result is cached for the
|
|
2055
2411
|
* lifetime of this `FMSOData` instance.
|
|
2056
2412
|
*
|
|
2057
|
-
* Returns the major version string (`'
|
|
2413
|
+
* Returns the major version string (`'20'`, `'21'`, `'22'`, `'26'`) or
|
|
2058
2414
|
* `'future'` if the version is newer than the spec knows about. Returns
|
|
2059
2415
|
* `null` if the version cannot be determined (e.g. the metadata lacks the
|
|
2060
2416
|
* annotation).
|
|
@@ -2138,6 +2494,75 @@ var FMSOData = class {
|
|
|
2138
2494
|
if (!v) return false;
|
|
2139
2495
|
return hasFeature(v, feature);
|
|
2140
2496
|
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Get a `SchemaEditor` handle for DDL operations (create/delete tables,
|
|
2499
|
+
* add/delete fields, create/delete indexes). Requires a FileMaker account
|
|
2500
|
+
* with full access privileges.
|
|
2501
|
+
*
|
|
2502
|
+
* ```ts
|
|
2503
|
+
* await db.schema().createTable({ tableName: 'Company', fields: [...] })
|
|
2504
|
+
* ```
|
|
2505
|
+
*/
|
|
2506
|
+
schema() {
|
|
2507
|
+
if (!this._schemaEditor) this._schemaEditor = new SchemaEditor(this);
|
|
2508
|
+
return this._schemaEditor;
|
|
2509
|
+
}
|
|
2510
|
+
/** Convenience: create a table. See {@link SchemaEditor#createTable}. */
|
|
2511
|
+
async createTable(params, opts = {}) {
|
|
2512
|
+
return this.schema().createTable(params, opts);
|
|
2513
|
+
}
|
|
2514
|
+
/** Convenience: add fields to a table. See {@link SchemaEditor#addFields}. */
|
|
2515
|
+
async addFields(params, opts = {}) {
|
|
2516
|
+
return this.schema().addFields(params, opts);
|
|
2517
|
+
}
|
|
2518
|
+
/** Convenience: delete a table (requires `confirm: true`). See {@link SchemaEditor#deleteTable}. */
|
|
2519
|
+
async deleteTable(tableName, opts) {
|
|
2520
|
+
return this.schema().deleteTable(tableName, opts);
|
|
2521
|
+
}
|
|
2522
|
+
/** Convenience: delete a field (requires `confirm: true`). See {@link SchemaEditor#deleteField}. */
|
|
2523
|
+
async deleteField(tableName, fieldName, opts) {
|
|
2524
|
+
return this.schema().deleteField(tableName, fieldName, opts);
|
|
2525
|
+
}
|
|
2526
|
+
/** Convenience: create an index. See {@link SchemaEditor#createIndex}. */
|
|
2527
|
+
async createIndex(tableName, fieldName, opts = {}) {
|
|
2528
|
+
return this.schema().createIndex(tableName, fieldName, opts);
|
|
2529
|
+
}
|
|
2530
|
+
/** Convenience: delete an index. See {@link SchemaEditor#deleteIndex}. */
|
|
2531
|
+
async deleteIndex(tableName, fieldName, opts = {}) {
|
|
2532
|
+
return this.schema().deleteIndex(tableName, fieldName, opts);
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* Get a `WebhookManager` handle for webhook CRUD operations (create, remove,
|
|
2536
|
+
* get, getAll, invoke). Requires FileMaker Server 2025+ (v22).
|
|
2537
|
+
*
|
|
2538
|
+
* ```ts
|
|
2539
|
+
* await db.webhooks().create({ webhook: 'https://...', tableName: 'contact' })
|
|
2540
|
+
* ```
|
|
2541
|
+
*/
|
|
2542
|
+
webhooks() {
|
|
2543
|
+
if (!this._webhookManager) this._webhookManager = new WebhookManager(this);
|
|
2544
|
+
return this._webhookManager;
|
|
2545
|
+
}
|
|
2546
|
+
/** Convenience: create a webhook. See {@link WebhookManager#create}. */
|
|
2547
|
+
async createWebhook(params, opts = {}) {
|
|
2548
|
+
return this.webhooks().create(params, opts);
|
|
2549
|
+
}
|
|
2550
|
+
/** Convenience: delete a webhook by ID. See {@link WebhookManager#remove}. */
|
|
2551
|
+
async removeWebhook(id, opts = {}) {
|
|
2552
|
+
return this.webhooks().remove(id, opts);
|
|
2553
|
+
}
|
|
2554
|
+
/** Convenience: get a webhook by ID. See {@link WebhookManager#get}. */
|
|
2555
|
+
async getWebhook(id, opts = {}) {
|
|
2556
|
+
return this.webhooks().get(id, opts);
|
|
2557
|
+
}
|
|
2558
|
+
/** Convenience: list all webhooks. See {@link WebhookManager#getAll}. */
|
|
2559
|
+
async getAllWebhooks(opts = {}) {
|
|
2560
|
+
return this.webhooks().getAll(opts);
|
|
2561
|
+
}
|
|
2562
|
+
/** Convenience: manually invoke a webhook by ID. See {@link WebhookManager#invoke}. */
|
|
2563
|
+
async invokeWebhook(id, opts = {}) {
|
|
2564
|
+
return this.webhooks().invoke(id, opts);
|
|
2565
|
+
}
|
|
2141
2566
|
/**
|
|
2142
2567
|
* Create a new `$batch` builder for composing multiple OData operations
|
|
2143
2568
|
* into a single HTTP round-trip.
|
|
@@ -2171,6 +2596,8 @@ export {
|
|
|
2171
2596
|
Changeset,
|
|
2172
2597
|
ContainerRef,
|
|
2173
2598
|
EntityRef,
|
|
2599
|
+
FIELD_DEFAULTS,
|
|
2600
|
+
FIELD_TYPES,
|
|
2174
2601
|
FMSOData as FMOData,
|
|
2175
2602
|
FMSODataError as FMODataError,
|
|
2176
2603
|
FMSOData,
|
|
@@ -2183,17 +2610,20 @@ export {
|
|
|
2183
2610
|
MetadataFetcher,
|
|
2184
2611
|
ODATA_PROTOCOL_VERSION,
|
|
2185
2612
|
Query,
|
|
2613
|
+
SchemaEditor,
|
|
2186
2614
|
ScriptInvoker,
|
|
2615
|
+
WebhookManager,
|
|
2187
2616
|
basicAuth,
|
|
2617
|
+
bearerAuth,
|
|
2188
2618
|
buildContainerJsonBody,
|
|
2189
2619
|
filterFactory,
|
|
2190
|
-
fmidAuth,
|
|
2191
2620
|
hasFeature,
|
|
2192
2621
|
hasQueryOption,
|
|
2193
2622
|
isFMSODataError as isFMODataError,
|
|
2194
2623
|
isFMSODataError,
|
|
2195
2624
|
isFMScriptError,
|
|
2196
2625
|
minVersionForFeature,
|
|
2626
|
+
parseFieldType,
|
|
2197
2627
|
parseServerVersion,
|
|
2198
2628
|
parseVersionString,
|
|
2199
2629
|
sniffContainerMime,
|