@tinacms/graphql 0.0.0-ecea7ac-20241011043815 → 0.0.0-ed6025e-20251201040055

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/dist/index.js CHANGED
@@ -17,14 +17,18 @@ var __copyProps = (to, from, except, desc) => {
17
17
  return to;
18
18
  };
19
19
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
20
24
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
21
25
  mod
22
26
  ));
23
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
28
 
25
29
  // src/index.ts
26
- var src_exports = {};
27
- __export(src_exports, {
30
+ var index_exports = {};
31
+ __export(index_exports, {
28
32
  AuditFileSystemBridge: () => AuditFileSystemBridge,
29
33
  Database: () => Database,
30
34
  FilesystemBridge: () => FilesystemBridge,
@@ -58,11 +62,11 @@ __export(src_exports, {
58
62
  transformDocument: () => transformDocument,
59
63
  transformDocumentIntoPayload: () => transformDocumentIntoPayload
60
64
  });
61
- module.exports = __toCommonJS(src_exports);
65
+ module.exports = __toCommonJS(index_exports);
62
66
 
63
67
  // src/build.ts
64
68
  var import_graphql2 = require("graphql");
65
- var import_lodash3 = __toESM(require("lodash.uniqby"));
69
+ var import_es_toolkit3 = require("es-toolkit");
66
70
 
67
71
  // src/util.ts
68
72
  var yup = __toESM(require("yup"));
@@ -118,7 +122,7 @@ var flattenDeep = (arr) => arr.flatMap(
118
122
  );
119
123
 
120
124
  // src/ast-builder/index.ts
121
- var import_lodash = __toESM(require("lodash.uniqby"));
125
+ var import_es_toolkit = require("es-toolkit");
122
126
  var SysFieldDefinition = {
123
127
  kind: "Field",
124
128
  name: {
@@ -130,6 +134,15 @@ var SysFieldDefinition = {
130
134
  selectionSet: {
131
135
  kind: "SelectionSet",
132
136
  selections: [
137
+ // {
138
+ // kind: 'Field' as const,
139
+ // name: {
140
+ // kind: 'Name' as const,
141
+ // value: 'title',
142
+ // },
143
+ // arguments: [],
144
+ // directives: [],
145
+ // },
133
146
  {
134
147
  kind: "Field",
135
148
  name: {
@@ -148,6 +161,15 @@ var SysFieldDefinition = {
148
161
  arguments: [],
149
162
  directives: []
150
163
  },
164
+ {
165
+ kind: "Field",
166
+ name: {
167
+ kind: "Name",
168
+ value: "hasReferences"
169
+ },
170
+ arguments: [],
171
+ directives: []
172
+ },
151
173
  {
152
174
  kind: "Field",
153
175
  name: {
@@ -188,6 +210,10 @@ var SysFieldDefinition = {
188
210
  }
189
211
  };
190
212
  var astBuilder = {
213
+ /**
214
+ * `FormFieldBuilder` acts as a shortcut to building an entire `ObjectTypeDefinition`, we use this
215
+ * because all Tina field objects share a common set of fields ('name', 'label', 'component')
216
+ */
191
217
  FormFieldBuilder: ({
192
218
  name,
193
219
  additionalFields
@@ -411,6 +437,8 @@ var astBuilder = {
411
437
  kind: "Name",
412
438
  value: name
413
439
  },
440
+ // @ts-ignore FIXME; this is being handled properly but we're lying to
441
+ // ts and then fixing it in the `extractInlineTypes` function
414
442
  fields
415
443
  }),
416
444
  UnionTypeDefinition: ({
@@ -423,6 +451,8 @@ var astBuilder = {
423
451
  value: name
424
452
  },
425
453
  directives: [],
454
+ // @ts-ignore FIXME; this is being handled properly but we're lying to
455
+ // ts and then fixing it in the `extractInlineTypes` function
426
456
  types: types.map((name2) => ({
427
457
  kind: "NamedType",
428
458
  name: {
@@ -519,8 +549,11 @@ var astBuilder = {
519
549
  string: "String",
520
550
  boolean: "Boolean",
521
551
  number: "Float",
552
+ // FIXME - needs to be float or int
522
553
  datetime: "String",
554
+ // FIXME
523
555
  image: "String",
556
+ // FIXME
524
557
  text: "String"
525
558
  };
526
559
  return scalars[type];
@@ -1004,12 +1037,13 @@ var astBuilder = {
1004
1037
  };
1005
1038
  },
1006
1039
  toGraphQLAst: (ast) => {
1007
- const definitions = (0, import_lodash.default)(
1040
+ const definitions = (0, import_es_toolkit.uniqBy)(
1008
1041
  [
1009
1042
  ...extractInlineTypes(ast.query),
1010
1043
  ...extractInlineTypes(ast.globalTemplates),
1011
1044
  ...ast.definitions
1012
1045
  ],
1046
+ // @ts-ignore - all nodes have a name property in practice
1013
1047
  (field) => field.name.value
1014
1048
  );
1015
1049
  return {
@@ -1019,8 +1053,7 @@ var astBuilder = {
1019
1053
  }
1020
1054
  };
1021
1055
  var capitalize = (s) => {
1022
- if (typeof s !== "string")
1023
- return "";
1056
+ if (typeof s !== "string") return "";
1024
1057
  return s.charAt(0).toUpperCase() + s.slice(1);
1025
1058
  };
1026
1059
  var extractInlineTypes = (item) => {
@@ -1033,7 +1066,7 @@ var extractInlineTypes = (item) => {
1033
1066
  const accumulator = [item];
1034
1067
  for (const node of walk(item)) {
1035
1068
  if (node.kind === "UnionTypeDefinition") {
1036
- node.types = (0, import_lodash.default)(node.types, (type) => type.name.value);
1069
+ node.types = (0, import_es_toolkit.uniqBy)(node.types, (type) => type.name.value);
1037
1070
  }
1038
1071
  if (node.kind === "NamedType") {
1039
1072
  if (typeof node.name.value !== "string") {
@@ -1063,41 +1096,6 @@ function* walk(maybeNode, visited = /* @__PURE__ */ new WeakSet()) {
1063
1096
  yield maybeNode;
1064
1097
  visited.add(maybeNode);
1065
1098
  }
1066
- function addNamespaceToSchema(maybeNode, namespace = []) {
1067
- if (typeof maybeNode === "string") {
1068
- return maybeNode;
1069
- }
1070
- if (typeof maybeNode === "boolean") {
1071
- return maybeNode;
1072
- }
1073
- const newNode = maybeNode;
1074
- const keys = Object.keys(maybeNode);
1075
- Object.values(maybeNode).map((m, index) => {
1076
- const key = keys[index];
1077
- if (Array.isArray(m)) {
1078
- newNode[key] = m.map((element) => {
1079
- if (!element) {
1080
- return;
1081
- }
1082
- if (!element.hasOwnProperty("name")) {
1083
- return element;
1084
- }
1085
- const value = element.name || element.value;
1086
- return addNamespaceToSchema(element, [...namespace, value]);
1087
- });
1088
- } else {
1089
- if (!m) {
1090
- return;
1091
- }
1092
- if (!m.hasOwnProperty("name")) {
1093
- newNode[key] = m;
1094
- } else {
1095
- newNode[key] = addNamespaceToSchema(m, [...namespace, m.name]);
1096
- }
1097
- }
1098
- });
1099
- return { ...newNode, namespace };
1100
- }
1101
1099
  var generateNamespacedFieldName = (names, suffix = "") => {
1102
1100
  return (suffix ? [...names, suffix] : names).map(capitalize).join("");
1103
1101
  };
@@ -1257,6 +1255,11 @@ var scalarDefinitions = [
1257
1255
  required: true,
1258
1256
  type: astBuilder.TYPES.String
1259
1257
  }),
1258
+ astBuilder.FieldDefinition({
1259
+ name: "hasReferences",
1260
+ required: false,
1261
+ type: astBuilder.TYPES.Boolean
1262
+ }),
1260
1263
  astBuilder.FieldDefinition({
1261
1264
  name: "breadcrumbs",
1262
1265
  required: true,
@@ -1433,13 +1436,12 @@ var checkPasswordHash = async ({
1433
1436
  return true;
1434
1437
  };
1435
1438
  var mapUserFields = (collectable, prefix = []) => {
1436
- var _a, _b, _c, _d, _e;
1437
1439
  const results = [];
1438
- const passwordFields = ((_a = collectable.fields) == null ? void 0 : _a.filter((field) => field.type === "password")) || [];
1440
+ const passwordFields = collectable.fields?.filter((field) => field.type === "password") || [];
1439
1441
  if (passwordFields.length > 1) {
1440
1442
  throw new Error("Only one password field is allowed");
1441
1443
  }
1442
- const idFields = ((_b = collectable.fields) == null ? void 0 : _b.filter((field) => field.uid)) || [];
1444
+ const idFields = collectable.fields?.filter((field) => field.uid) || [];
1443
1445
  if (idFields.length > 1) {
1444
1446
  throw new Error("Only one uid field is allowed");
1445
1447
  }
@@ -1447,11 +1449,11 @@ var mapUserFields = (collectable, prefix = []) => {
1447
1449
  results.push({
1448
1450
  path: prefix,
1449
1451
  collectable,
1450
- idFieldName: (_c = idFields[0]) == null ? void 0 : _c.name,
1451
- passwordFieldName: (_d = passwordFields[0]) == null ? void 0 : _d.name
1452
+ idFieldName: idFields[0]?.name,
1453
+ passwordFieldName: passwordFields[0]?.name
1452
1454
  });
1453
1455
  }
1454
- (_e = collectable.fields) == null ? void 0 : _e.forEach((field) => {
1456
+ collectable.fields?.forEach((field) => {
1455
1457
  if (field.type === "object" && field.fields) {
1456
1458
  results.push(...mapUserFields(field, [...prefix, field.name]));
1457
1459
  }
@@ -1471,6 +1473,19 @@ var Builder = class {
1471
1473
  this.addToLookupMap = (lookup) => {
1472
1474
  this.lookupMap[lookup.type] = lookup;
1473
1475
  };
1476
+ /**
1477
+ * ```graphql
1478
+ * # ex.
1479
+ * {
1480
+ * getCollection(collection: $collection) {
1481
+ * name
1482
+ * documents {...}
1483
+ * }
1484
+ * }
1485
+ * ```
1486
+ *
1487
+ * @param collections
1488
+ */
1474
1489
  this.buildCollectionDefinition = async (collections) => {
1475
1490
  const name = "collection";
1476
1491
  const typeName = "Collection";
@@ -1541,6 +1556,19 @@ var Builder = class {
1541
1556
  required: true
1542
1557
  });
1543
1558
  };
1559
+ /**
1560
+ * ```graphql
1561
+ * # ex.
1562
+ * {
1563
+ * getCollections {
1564
+ * name
1565
+ * documents {...}
1566
+ * }
1567
+ * }
1568
+ * ```
1569
+ *
1570
+ * @param collections
1571
+ */
1544
1572
  this.buildMultiCollectionDefinition = async (collections) => {
1545
1573
  const name = "collections";
1546
1574
  const typeName = "Collection";
@@ -1551,6 +1579,17 @@ var Builder = class {
1551
1579
  required: true
1552
1580
  });
1553
1581
  };
1582
+ /**
1583
+ * ```graphql
1584
+ * # ex.
1585
+ * {
1586
+ * node(id: $id) {
1587
+ * id
1588
+ * data {...}
1589
+ * }
1590
+ * }
1591
+ * ```
1592
+ */
1554
1593
  this.multiNodeDocument = async () => {
1555
1594
  const name = "node";
1556
1595
  const args = [
@@ -1571,6 +1610,19 @@ var Builder = class {
1571
1610
  required: true
1572
1611
  });
1573
1612
  };
1613
+ /**
1614
+ * ```graphql
1615
+ * # ex.
1616
+ * {
1617
+ * getDocument(collection: $collection, relativePath: $relativePath) {
1618
+ * id
1619
+ * data {...}
1620
+ * }
1621
+ * }
1622
+ * ```
1623
+ *
1624
+ * @param collections
1625
+ */
1574
1626
  this.multiCollectionDocument = async (collections) => {
1575
1627
  const name = "document";
1576
1628
  const args = [
@@ -1596,6 +1648,19 @@ var Builder = class {
1596
1648
  required: true
1597
1649
  });
1598
1650
  };
1651
+ /**
1652
+ * ```graphql
1653
+ * # ex.
1654
+ * {
1655
+ * addPendingDocument(collection: $collection, relativePath: $relativePath, params: $params) {
1656
+ * id
1657
+ * data {...}
1658
+ * }
1659
+ * }
1660
+ * ```
1661
+ *
1662
+ * @param collections
1663
+ */
1599
1664
  this.addMultiCollectionDocumentMutation = async () => {
1600
1665
  return astBuilder.FieldDefinition({
1601
1666
  name: "addPendingDocument",
@@ -1620,6 +1685,19 @@ var Builder = class {
1620
1685
  type: astBuilder.TYPES.MultiCollectionDocument
1621
1686
  });
1622
1687
  };
1688
+ /**
1689
+ * ```graphql
1690
+ * # ex.
1691
+ * {
1692
+ * createDocument(relativePath: $relativePath, params: $params) {
1693
+ * id
1694
+ * data {...}
1695
+ * }
1696
+ * }
1697
+ * ```
1698
+ *
1699
+ * @param collections
1700
+ */
1623
1701
  this.buildCreateCollectionDocumentMutation = async (collections) => {
1624
1702
  return astBuilder.FieldDefinition({
1625
1703
  name: "createDocument",
@@ -1647,6 +1725,19 @@ var Builder = class {
1647
1725
  type: astBuilder.TYPES.MultiCollectionDocument
1648
1726
  });
1649
1727
  };
1728
+ /**
1729
+ * ```graphql
1730
+ * # ex.
1731
+ * {
1732
+ * updateDocument(relativePath: $relativePath, params: $params) {
1733
+ * id
1734
+ * data {...}
1735
+ * }
1736
+ * }
1737
+ * ```
1738
+ *
1739
+ * @param collections
1740
+ */
1650
1741
  this.buildUpdateCollectionDocumentMutation = async (collections) => {
1651
1742
  return astBuilder.FieldDefinition({
1652
1743
  name: "updateDocument",
@@ -1674,6 +1765,19 @@ var Builder = class {
1674
1765
  type: astBuilder.TYPES.MultiCollectionDocument
1675
1766
  });
1676
1767
  };
1768
+ /**
1769
+ * ```graphql
1770
+ * # ex.
1771
+ * {
1772
+ * deleteDocument(relativePath: $relativePath, params: $params) {
1773
+ * id
1774
+ * data {...}
1775
+ * }
1776
+ * }
1777
+ * ```
1778
+ *
1779
+ * @param collections
1780
+ */
1677
1781
  this.buildDeleteCollectionDocumentMutation = async (collections) => {
1678
1782
  return astBuilder.FieldDefinition({
1679
1783
  name: "deleteDocument",
@@ -1693,6 +1797,19 @@ var Builder = class {
1693
1797
  type: astBuilder.TYPES.MultiCollectionDocument
1694
1798
  });
1695
1799
  };
1800
+ /**
1801
+ * ```graphql
1802
+ * # ex.
1803
+ * {
1804
+ * createFolder(folderName: $folderName, params: $params) {
1805
+ * id
1806
+ * data {...}
1807
+ * }
1808
+ * }
1809
+ * ```
1810
+ *
1811
+ * @param collections
1812
+ */
1696
1813
  this.buildCreateCollectionFolderMutation = async () => {
1697
1814
  return astBuilder.FieldDefinition({
1698
1815
  name: "createFolder",
@@ -1712,6 +1829,19 @@ var Builder = class {
1712
1829
  type: astBuilder.TYPES.MultiCollectionDocument
1713
1830
  });
1714
1831
  };
1832
+ /**
1833
+ * ```graphql
1834
+ * # ex.
1835
+ * {
1836
+ * getPostDocument(relativePath: $relativePath) {
1837
+ * id
1838
+ * data {...}
1839
+ * }
1840
+ * }
1841
+ * ```
1842
+ *
1843
+ * @param collection
1844
+ */
1715
1845
  this.collectionDocument = async (collection) => {
1716
1846
  const name = NAMER.queryName([collection.name]);
1717
1847
  const type = await this._buildCollectionDocumentType(collection);
@@ -1772,6 +1902,20 @@ var Builder = class {
1772
1902
  const args = [];
1773
1903
  return astBuilder.FieldDefinition({ type, name, args, required: false });
1774
1904
  };
1905
+ /**
1906
+ * Turns a collection into a fragment that gets updated on build. This fragment does not resolve references
1907
+ * ```graphql
1908
+ * # ex.
1909
+ * fragment AuthorsParts on Authors {
1910
+ * name
1911
+ * avatar
1912
+ * ...
1913
+ * }
1914
+ * ```
1915
+ *
1916
+ * @public
1917
+ * @param collection a TinaCloud collection
1918
+ */
1775
1919
  this.collectionFragment = async (collection) => {
1776
1920
  const name = NAMER.dataTypeName(collection.namespace);
1777
1921
  const fragmentName = NAMER.fragmentName(collection.namespace);
@@ -1785,14 +1929,27 @@ var Builder = class {
1785
1929
  selections: filterSelections(selections)
1786
1930
  });
1787
1931
  };
1932
+ /**
1933
+ * Given a collection this function returns its selections set. For example for Post this would return
1934
+ *
1935
+ * "
1936
+ * body
1937
+ * title
1938
+ * ... on Author {
1939
+ * name
1940
+ * heroImg
1941
+ * }
1942
+ *
1943
+ * But in the AST format
1944
+ *
1945
+ * */
1788
1946
  this._getCollectionFragmentSelections = async (collection, depth) => {
1789
- var _a;
1790
1947
  const selections = [];
1791
1948
  selections.push({
1792
1949
  name: { kind: "Name", value: "__typename" },
1793
1950
  kind: "Field"
1794
1951
  });
1795
- if (((_a = collection.fields) == null ? void 0 : _a.length) > 0) {
1952
+ if (collection.fields?.length > 0) {
1796
1953
  await sequential(collection.fields, async (x) => {
1797
1954
  const field = await this._buildFieldNodeForFragments(x, depth);
1798
1955
  selections.push(field);
@@ -1807,7 +1964,6 @@ var Builder = class {
1807
1964
  return selections;
1808
1965
  };
1809
1966
  this._buildFieldNodeForFragments = async (field, depth) => {
1810
- var _a, _b;
1811
1967
  switch (field.type) {
1812
1968
  case "string":
1813
1969
  case "image":
@@ -1840,7 +1996,7 @@ var Builder = class {
1840
1996
  selections: filterSelections([passwordValue, passwordChangeRequired])
1841
1997
  });
1842
1998
  case "object":
1843
- if (((_a = field.fields) == null ? void 0 : _a.length) > 0) {
1999
+ if (field.fields?.length > 0) {
1844
2000
  const selections2 = [];
1845
2001
  await sequential(field.fields, async (item) => {
1846
2002
  const field2 = await this._buildFieldNodeForFragments(item, depth);
@@ -1853,7 +2009,7 @@ var Builder = class {
1853
2009
  ...filterSelections(selections2)
1854
2010
  ]
1855
2011
  });
1856
- } else if (((_b = field.templates) == null ? void 0 : _b.length) > 0) {
2012
+ } else if (field.templates?.length > 0) {
1857
2013
  const selections2 = [];
1858
2014
  await sequential(field.templates, async (tem) => {
1859
2015
  if (typeof tem === "object") {
@@ -1868,9 +2024,9 @@ var Builder = class {
1868
2024
  ]
1869
2025
  });
1870
2026
  }
2027
+ // TODO: Should we throw here?
1871
2028
  case "reference":
1872
- if (depth >= this.maxDepth)
1873
- return false;
2029
+ if (depth >= this.maxDepth) return false;
1874
2030
  if (!("collections" in field)) {
1875
2031
  return false;
1876
2032
  }
@@ -1902,6 +2058,7 @@ var Builder = class {
1902
2058
  name: field.name,
1903
2059
  selections: [
1904
2060
  ...selections,
2061
+ // This is ... on Document { id }
1905
2062
  {
1906
2063
  kind: "InlineFragment",
1907
2064
  typeCondition: {
@@ -1932,6 +2089,19 @@ var Builder = class {
1932
2089
  });
1933
2090
  }
1934
2091
  };
2092
+ /**
2093
+ * ```graphql
2094
+ * # ex.
2095
+ * mutation {
2096
+ * updatePostDocument(relativePath: $relativePath, params: $params) {
2097
+ * id
2098
+ * data {...}
2099
+ * }
2100
+ * }
2101
+ * ```
2102
+ *
2103
+ * @param collection
2104
+ */
1935
2105
  this.updateCollectionDocumentMutation = async (collection) => {
1936
2106
  return astBuilder.FieldDefinition({
1937
2107
  type: await this._buildCollectionDocumentType(collection),
@@ -1951,6 +2121,19 @@ var Builder = class {
1951
2121
  ]
1952
2122
  });
1953
2123
  };
2124
+ /**
2125
+ * ```graphql
2126
+ * # ex.
2127
+ * mutation {
2128
+ * createPostDocument(relativePath: $relativePath, params: $params) {
2129
+ * id
2130
+ * data {...}
2131
+ * }
2132
+ * }
2133
+ * ```
2134
+ *
2135
+ * @param collection
2136
+ */
1954
2137
  this.createCollectionDocumentMutation = async (collection) => {
1955
2138
  return astBuilder.FieldDefinition({
1956
2139
  type: await this._buildCollectionDocumentType(collection),
@@ -1970,6 +2153,22 @@ var Builder = class {
1970
2153
  ]
1971
2154
  });
1972
2155
  };
2156
+ /**
2157
+ * ```graphql
2158
+ * # ex.
2159
+ * {
2160
+ * getPostList(first: 10) {
2161
+ * edges {
2162
+ * node {
2163
+ * id
2164
+ * }
2165
+ * }
2166
+ * }
2167
+ * }
2168
+ * ```
2169
+ *
2170
+ * @param collection
2171
+ */
1973
2172
  this.collectionDocumentList = async (collection) => {
1974
2173
  const connectionName = NAMER.referenceConnectionType(collection.namespace);
1975
2174
  this.addToLookupMap({
@@ -1985,6 +2184,10 @@ var Builder = class {
1985
2184
  collection
1986
2185
  });
1987
2186
  };
2187
+ /**
2188
+ * GraphQL type definitions which remain unchanged regardless
2189
+ * of the supplied Tina schema. Ex. "node" interface
2190
+ */
1988
2191
  this.buildStaticDefinitions = () => staticDefinitions;
1989
2192
  this._buildCollectionDocumentType = async (collection, suffix = "", extraFields = [], extraInterfaces = []) => {
1990
2193
  const documentTypeName = NAMER.documentTypeName(collection.namespace);
@@ -2468,7 +2671,7 @@ var Builder = class {
2468
2671
  this.addToLookupMap({
2469
2672
  type: name,
2470
2673
  resolveType: "unionData",
2471
- collection: collection == null ? void 0 : collection.name,
2674
+ collection: collection?.name,
2472
2675
  typeMap
2473
2676
  });
2474
2677
  return astBuilder.UnionTypeDefinition({ name, types });
@@ -2489,6 +2692,7 @@ var Builder = class {
2489
2692
  name: NAMER.dataFilterTypeName(namespace),
2490
2693
  fields: await sequential(collections, async (collection2) => {
2491
2694
  return astBuilder.InputValueDefinition({
2695
+ // @ts-ignore
2492
2696
  name: collection2.name,
2493
2697
  type: NAMER.dataFilterTypeName(collection2.namespace)
2494
2698
  });
@@ -2574,7 +2778,7 @@ var Builder = class {
2574
2778
  this._buildDataField = async (field) => {
2575
2779
  const listWarningMsg = `
2576
2780
  WARNING: The user interface for ${field.type} does not support \`list: true\`
2577
- Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2781
+ Visit https://tina.io/docs/r/content-fields/#list-fields/ for more information
2578
2782
 
2579
2783
  `;
2580
2784
  switch (field.type) {
@@ -2677,8 +2881,8 @@ Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2677
2881
  ]
2678
2882
  });
2679
2883
  };
2680
- var _a, _b, _c, _d;
2681
- this.maxDepth = (_d = (_c = (_b = (_a = config == null ? void 0 : config.tinaSchema.schema) == null ? void 0 : _a.config) == null ? void 0 : _b.client) == null ? void 0 : _c.referenceDepth) != null ? _d : 2;
2884
+ this.maxDepth = // @ts-ignore
2885
+ config?.tinaSchema.schema?.config?.client?.referenceDepth ?? 2;
2682
2886
  this.tinaSchema = config.tinaSchema;
2683
2887
  this.lookupMap = {};
2684
2888
  }
@@ -2689,8 +2893,7 @@ Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2689
2893
  selections.push(field);
2690
2894
  });
2691
2895
  const filteredSelections = filterSelections(selections);
2692
- if (!filteredSelections.length)
2693
- return false;
2896
+ if (!filteredSelections.length) return false;
2694
2897
  return astBuilder.InlineFragmentDefinition({
2695
2898
  selections: filteredSelections,
2696
2899
  name: NAMER.dataTypeName(template.namespace)
@@ -2724,12 +2927,13 @@ var filterSelections = (arr) => {
2724
2927
  };
2725
2928
 
2726
2929
  // src/schema/createSchema.ts
2727
- var import_schema_tools2 = require("@tinacms/schema-tools");
2930
+ var import_schema_tools3 = require("@tinacms/schema-tools");
2728
2931
 
2729
2932
  // src/schema/validate.ts
2730
- var import_lodash2 = __toESM(require("lodash.clonedeep"));
2731
- var yup2 = __toESM(require("yup"));
2732
2933
  var import_schema_tools = require("@tinacms/schema-tools");
2934
+ var yup2 = __toESM(require("yup"));
2935
+ var import_es_toolkit2 = require("es-toolkit");
2936
+ var import_schema_tools2 = require("@tinacms/schema-tools");
2733
2937
  var FIELD_TYPES = [
2734
2938
  "string",
2735
2939
  "number",
@@ -2742,8 +2946,8 @@ var FIELD_TYPES = [
2742
2946
  "password"
2743
2947
  ];
2744
2948
  var validateSchema = async (schema) => {
2745
- const schema2 = addNamespaceToSchema(
2746
- (0, import_lodash2.default)(schema)
2949
+ const schema2 = (0, import_schema_tools.addNamespaceToSchema)(
2950
+ (0, import_es_toolkit2.cloneDeep)(schema)
2747
2951
  );
2748
2952
  const collections = await sequential(
2749
2953
  schema2.collections,
@@ -2751,7 +2955,7 @@ var validateSchema = async (schema) => {
2751
2955
  );
2752
2956
  validationCollectionsPathAndMatch(collections);
2753
2957
  if (schema2.config) {
2754
- const config = (0, import_schema_tools.validateTinaCloudSchemaConfig)(schema2.config);
2958
+ const config = (0, import_schema_tools2.validateTinaCloudSchemaConfig)(schema2.config);
2755
2959
  return {
2756
2960
  collections,
2757
2961
  config
@@ -2767,20 +2971,18 @@ var validationCollectionsPathAndMatch = (collections) => {
2767
2971
  return;
2768
2972
  }
2769
2973
  const noMatchCollections = collections.filter((x) => {
2770
- return typeof (x == null ? void 0 : x.match) === "undefined";
2974
+ return typeof x?.match === "undefined";
2771
2975
  }).map((x) => `${x.path}${x.format || "md"}`);
2772
2976
  if (noMatchCollections.length !== new Set(noMatchCollections).size) {
2773
2977
  throw new Error(
2978
+ // TODO: add a link to the docs
2774
2979
  "Two collections without match can not have the same `path`. Please make the `path` unique or add a matches property to the collection."
2775
2980
  );
2776
2981
  }
2777
2982
  const hasMatchAndPath = collections.filter((x) => {
2778
2983
  return typeof x.path !== "undefined" && typeof x.match !== "undefined";
2779
2984
  }).map(
2780
- (x) => {
2781
- var _a, _b;
2782
- return `${x.path}|${((_a = x == null ? void 0 : x.match) == null ? void 0 : _a.exclude) || ""}|${((_b = x == null ? void 0 : x.match) == null ? void 0 : _b.include) || ""}|${x.format || "md"}`;
2783
- }
2985
+ (x) => `${x.path}|${x?.match?.exclude || ""}|${x?.match?.include || ""}|${x.format || "md"}`
2784
2986
  );
2785
2987
  if (hasMatchAndPath.length !== new Set(hasMatchAndPath).size) {
2786
2988
  throw new Error(
@@ -2804,7 +3006,7 @@ var validationCollectionsPathAndMatch = (collections) => {
2804
3006
  );
2805
3007
  }
2806
3008
  const matches = collectionsArr.map(
2807
- (x) => typeof (x == null ? void 0 : x.match) === "object" ? JSON.stringify(x.match) : ""
3009
+ (x) => typeof x?.match === "object" ? JSON.stringify(x.match) : ""
2808
3010
  );
2809
3011
  if (matches.length === new Set(matches).size) {
2810
3012
  return;
@@ -2882,7 +3084,7 @@ var validateField = async (field) => {
2882
3084
  // package.json
2883
3085
  var package_default = {
2884
3086
  name: "@tinacms/graphql",
2885
- version: "1.5.5",
3087
+ version: "1.6.3",
2886
3088
  main: "dist/index.js",
2887
3089
  module: "dist/index.mjs",
2888
3090
  typings: "dist/index.d.ts",
@@ -2908,34 +3110,31 @@ var package_default = {
2908
3110
  types: "pnpm tsc",
2909
3111
  build: "tinacms-scripts build",
2910
3112
  docs: "pnpm typedoc",
2911
- serve: "pnpm nodemon dist/server.js",
2912
- test: "jest",
2913
- "test-watch": "jest --watch"
3113
+ test: "vitest run",
3114
+ "test-watch": "vitest"
2914
3115
  },
2915
3116
  dependencies: {
2916
- "@iarna/toml": "^2.2.5",
3117
+ "@iarna/toml": "catalog:",
2917
3118
  "@tinacms/mdx": "workspace:*",
2918
3119
  "@tinacms/schema-tools": "workspace:*",
2919
- "abstract-level": "^1.0.4",
3120
+ "abstract-level": "catalog:",
2920
3121
  "date-fns": "^2.30.0",
2921
- "fast-glob": "^3.3.2",
2922
- "fs-extra": "^11.2.0",
2923
- "glob-parent": "^6.0.2",
3122
+ "es-toolkit": "^1.42.0",
3123
+ "fast-glob": "catalog:",
3124
+ "fs-extra": "catalog:",
3125
+ "glob-parent": "catalog:",
2924
3126
  graphql: "15.8.0",
2925
- "gray-matter": "^4.0.3",
2926
- "isomorphic-git": "^1.27.1",
2927
- "js-sha1": "^0.6.0",
3127
+ "gray-matter": "catalog:",
3128
+ "isomorphic-git": "catalog:",
3129
+ "js-sha1": "catalog:",
2928
3130
  "js-yaml": "^3.14.1",
2929
- "jsonpath-plus": "^6.0.1",
2930
- "lodash.clonedeep": "^4.5.0",
2931
- "lodash.set": "^4.3.2",
2932
- "lodash.uniqby": "^4.7.0",
2933
- "many-level": "^2.0.0",
2934
- micromatch: "4.0.8",
2935
- "normalize-path": "^3.0.0",
2936
- "readable-stream": "^4.5.2",
2937
- scmp: "^2.1.0",
2938
- yup: "^0.32.11"
3131
+ "jsonpath-plus": "catalog:",
3132
+ "many-level": "catalog:",
3133
+ micromatch: "catalog:",
3134
+ "normalize-path": "catalog:",
3135
+ "readable-stream": "catalog:",
3136
+ scmp: "catalog:",
3137
+ yup: "^1.6.1"
2939
3138
  },
2940
3139
  publishConfig: {
2941
3140
  registry: "https://registry.npmjs.org"
@@ -2949,26 +3148,21 @@ var package_default = {
2949
3148
  "@tinacms/scripts": "workspace:*",
2950
3149
  "@types/cors": "^2.8.17",
2951
3150
  "@types/estree": "^0.0.50",
2952
- "@types/express": "^4.17.21",
3151
+ "@types/express": "catalog:",
2953
3152
  "@types/fs-extra": "^9.0.13",
2954
- "@types/jest": "^26.0.24",
2955
3153
  "@types/js-yaml": "^3.12.10",
2956
- "@types/lodash.camelcase": "^4.3.9",
2957
- "@types/lodash.upperfirst": "^4.3.9",
2958
- "@types/lru-cache": "^5.1.1",
2959
- "@types/mdast": "^3.0.15",
2960
- "@types/micromatch": "^4.0.9",
2961
- "@types/node": "^22.7.4",
2962
- "@types/normalize-path": "^3.0.2",
2963
- "@types/ws": "^7.4.7",
2964
- "@types/yup": "^0.29.14",
2965
- jest: "^29.7.0",
2966
- "jest-diff": "^29.7.0",
3154
+ "@types/lru-cache": "catalog:",
3155
+ "@types/mdast": "catalog:",
3156
+ "@types/micromatch": "catalog:",
3157
+ "@types/node": "^22.13.1",
3158
+ "@types/normalize-path": "catalog:",
3159
+ "@types/ws": "catalog:",
2967
3160
  "jest-file-snapshot": "^0.5.0",
2968
- "jest-matcher-utils": "^29.7.0",
2969
- "memory-level": "^1.0.0",
2970
- nodemon: "3.1.4",
2971
- typescript: "^5.6.2"
3161
+ "memory-level": "catalog:",
3162
+ typescript: "^5.7.3",
3163
+ vite: "^4.5.9",
3164
+ vitest: "^0.32.4",
3165
+ zod: "catalog:"
2972
3166
  }
2973
3167
  };
2974
3168
 
@@ -2983,7 +3177,7 @@ var createSchema = async ({
2983
3177
  if (flags && flags.length > 0) {
2984
3178
  meta["flags"] = flags;
2985
3179
  }
2986
- return new import_schema_tools2.TinaSchema({
3180
+ return new import_schema_tools3.TinaSchema({
2987
3181
  version: {
2988
3182
  fullVersion: package_default.version,
2989
3183
  major,
@@ -3038,8 +3232,9 @@ var _buildFragments = async (builder, tinaSchema) => {
3038
3232
  });
3039
3233
  const fragDoc = {
3040
3234
  kind: "Document",
3041
- definitions: (0, import_lodash3.default)(
3235
+ definitions: (0, import_es_toolkit3.uniqBy)(
3042
3236
  extractInlineTypes(fragmentDefinitionsFields),
3237
+ // @ts-ignore - all nodes returned by extractInlineTypes have a name property
3043
3238
  (node) => node.name.value
3044
3239
  )
3045
3240
  };
@@ -3049,7 +3244,6 @@ var _buildQueries = async (builder, tinaSchema) => {
3049
3244
  const operationsDefinitions = [];
3050
3245
  const collections = tinaSchema.getCollections();
3051
3246
  await sequential(collections, async (collection) => {
3052
- var _a, _b, _c;
3053
3247
  const queryName = NAMER.queryName(collection.namespace);
3054
3248
  const queryListName = NAMER.generateQueryListName(collection.namespace);
3055
3249
  const queryFilterTypeName = NAMER.dataFilterTypeName(collection.namespace);
@@ -3062,16 +3256,18 @@ var _buildQueries = async (builder, tinaSchema) => {
3062
3256
  fragName,
3063
3257
  queryName: queryListName,
3064
3258
  filterType: queryFilterTypeName,
3259
+ // look for flag to see if the data layer is enabled
3065
3260
  dataLayer: Boolean(
3066
- (_c = (_b = (_a = tinaSchema.config) == null ? void 0 : _a.meta) == null ? void 0 : _b.flags) == null ? void 0 : _c.find((x) => x === "experimentalData")
3261
+ tinaSchema.config?.meta?.flags?.find((x) => x === "experimentalData")
3067
3262
  )
3068
3263
  })
3069
3264
  );
3070
3265
  });
3071
3266
  const queryDoc = {
3072
3267
  kind: "Document",
3073
- definitions: (0, import_lodash3.default)(
3268
+ definitions: (0, import_es_toolkit3.uniqBy)(
3074
3269
  extractInlineTypes(operationsDefinitions),
3270
+ // @ts-ignore - all nodes returned by extractInlineTypes have a name property
3075
3271
  (node) => node.name.value
3076
3272
  )
3077
3273
  };
@@ -3122,7 +3318,9 @@ var _buildSchema = async (builder, tinaSchema) => {
3122
3318
  await builder.buildCreateCollectionFolderMutation()
3123
3319
  );
3124
3320
  await sequential(collections, async (collection) => {
3125
- queryTypeDefinitionFields.push(await builder.collectionDocument(collection));
3321
+ queryTypeDefinitionFields.push(
3322
+ await builder.collectionDocument(collection)
3323
+ );
3126
3324
  if (collection.isAuthCollection) {
3127
3325
  queryTypeDefinitionFields.push(
3128
3326
  await builder.authenticationCollectionDocument(collection)
@@ -3156,13 +3354,15 @@ var _buildSchema = async (builder, tinaSchema) => {
3156
3354
  fields: mutationTypeDefinitionFields
3157
3355
  })
3158
3356
  );
3159
- return {
3357
+ const schema = {
3160
3358
  kind: "Document",
3161
- definitions: (0, import_lodash3.default)(
3359
+ definitions: (0, import_es_toolkit3.uniqBy)(
3162
3360
  extractInlineTypes(definitions),
3361
+ // @ts-ignore - all nodes returned by extractInlineTypes have a name property
3163
3362
  (node) => node.name.value
3164
3363
  )
3165
3364
  };
3365
+ return schema;
3166
3366
  };
3167
3367
 
3168
3368
  // src/resolve.ts
@@ -3171,395 +3371,156 @@ var import_graphql5 = require("graphql");
3171
3371
  // src/resolver/index.ts
3172
3372
  var import_path3 = __toESM(require("path"));
3173
3373
  var import_isValid = __toESM(require("date-fns/isValid/index.js"));
3374
+ var import_jsonpath_plus2 = require("jsonpath-plus");
3174
3375
 
3175
3376
  // src/mdx/index.ts
3176
3377
  var import_mdx = require("@tinacms/mdx");
3177
3378
 
3178
- // src/resolver/error.ts
3179
- var TinaGraphQLError = class extends Error {
3180
- constructor(message, extensions) {
3181
- super(message);
3182
- if (!this.name) {
3183
- Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
3184
- }
3185
- this.extensions = { ...extensions };
3186
- }
3379
+ // src/resolver/index.ts
3380
+ var import_graphql3 = require("graphql");
3381
+
3382
+ // src/database/datalayer.ts
3383
+ var import_jsonpath_plus = require("jsonpath-plus");
3384
+ var import_js_sha1 = __toESM(require("js-sha1"));
3385
+
3386
+ // src/database/level.ts
3387
+ var ARRAY_ITEM_VALUE_SEPARATOR = ",";
3388
+ var INDEX_KEY_FIELD_SEPARATOR = "";
3389
+ var CONTENT_ROOT_PREFIX = "~";
3390
+ var SUBLEVEL_OPTIONS = {
3391
+ separator: INDEX_KEY_FIELD_SEPARATOR,
3392
+ valueEncoding: "json"
3187
3393
  };
3188
- var TinaFetchError = class extends Error {
3189
- constructor(message, args) {
3190
- super(message);
3191
- this.name = "TinaFetchError";
3192
- this.collection = args.collection;
3193
- this.stack = args.stack;
3194
- this.file = args.file;
3195
- this.originalError = args.originalError;
3394
+ var LevelProxyHandler = {
3395
+ get: function(target, property) {
3396
+ if (!target[property]) {
3397
+ throw new Error(`The property, ${property.toString()}, doesn't exist`);
3398
+ }
3399
+ if (typeof target[property] !== "function") {
3400
+ throw new Error(
3401
+ `The property, ${property.toString()}, is not a function`
3402
+ );
3403
+ }
3404
+ if (property === "get") {
3405
+ return async (...args) => {
3406
+ let result;
3407
+ try {
3408
+ result = await target[property].apply(target, args);
3409
+ } catch (e) {
3410
+ if (e.code !== "LEVEL_NOT_FOUND") {
3411
+ throw e;
3412
+ }
3413
+ }
3414
+ return result;
3415
+ };
3416
+ } else if (property === "sublevel") {
3417
+ return (...args) => {
3418
+ return new Proxy(
3419
+ // eslint-disable-next-line prefer-spread
3420
+ target[property].apply(target, args),
3421
+ LevelProxyHandler
3422
+ );
3423
+ };
3424
+ } else {
3425
+ return (...args) => target[property].apply(target, args);
3426
+ }
3196
3427
  }
3197
3428
  };
3198
- var TinaQueryError = class extends TinaFetchError {
3199
- constructor(args) {
3200
- super(
3201
- `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3202
- args
3203
- );
3429
+ var LevelProxy = class {
3430
+ constructor(level) {
3431
+ return new Proxy(level, LevelProxyHandler);
3204
3432
  }
3205
3433
  };
3206
- var TinaParseDocumentError = class extends TinaFetchError {
3207
- constructor(args) {
3208
- super(
3209
- `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3210
- args
3434
+
3435
+ // src/database/datalayer.ts
3436
+ var import_path2 = __toESM(require("path"));
3437
+
3438
+ // src/database/util.ts
3439
+ var import_toml = __toESM(require("@iarna/toml"));
3440
+ var import_schema_tools4 = require("@tinacms/schema-tools");
3441
+ var import_gray_matter = __toESM(require("gray-matter"));
3442
+ var import_js_yaml = __toESM(require("js-yaml"));
3443
+ var import_path = __toESM(require("path"));
3444
+ var import_micromatch = __toESM(require("micromatch"));
3445
+
3446
+ // src/database/alias-utils.ts
3447
+ var replaceBlockAliases = (template, item) => {
3448
+ const output = { ...item };
3449
+ const templateKey = template.templateKey || "_template";
3450
+ const templateName = output[templateKey];
3451
+ const matchingTemplate = template.templates.find(
3452
+ (t) => t.nameOverride == templateName || t.name == templateName
3453
+ );
3454
+ if (!matchingTemplate) {
3455
+ throw new Error(
3456
+ `Block template "${templateName}" is not defined for field "${template.name}"`
3211
3457
  );
3212
3458
  }
3213
- toString() {
3214
- return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
3459
+ output._template = matchingTemplate.name;
3460
+ if (templateKey != "_template") {
3461
+ delete output[templateKey];
3215
3462
  }
3463
+ return output;
3216
3464
  };
3217
- var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
3218
- var handleFetchErrorError = (e, verbose) => {
3219
- if (e instanceof Error) {
3220
- if (e instanceof TinaFetchError) {
3221
- if (verbose) {
3222
- console.log(e.toString());
3223
- console.log(e);
3224
- console.log(e.stack);
3465
+ var replaceNameOverrides = (template, obj) => {
3466
+ if (template.list) {
3467
+ return obj.map((item) => {
3468
+ if (isBlockField(template)) {
3469
+ item = replaceBlockAliases(template, item);
3225
3470
  }
3226
- }
3471
+ return _replaceNameOverrides(
3472
+ getTemplateForData(template, item).fields,
3473
+ item
3474
+ );
3475
+ });
3227
3476
  } else {
3228
- console.error(e);
3477
+ return _replaceNameOverrides(getTemplateForData(template, obj).fields, obj);
3229
3478
  }
3230
- throw e;
3231
3479
  };
3232
-
3233
- // src/resolver/filter-utils.ts
3234
- var resolveReferences = async (filter, fields, resolver) => {
3235
- for (const fieldKey of Object.keys(filter)) {
3236
- const fieldDefinition = fields.find(
3237
- (f) => f.name === fieldKey
3480
+ function isBlockField(field) {
3481
+ return field && field.type === "object" && field.templates?.length > 0;
3482
+ }
3483
+ var _replaceNameOverrides = (fields, obj) => {
3484
+ const output = {};
3485
+ Object.keys(obj).forEach((key) => {
3486
+ const field = fields.find(
3487
+ (fieldWithMatchingAlias) => (fieldWithMatchingAlias?.nameOverride || fieldWithMatchingAlias?.name) === key
3238
3488
  );
3239
- if (fieldDefinition) {
3240
- if (fieldDefinition.type === "reference") {
3241
- const { edges, values } = await resolver(filter, fieldDefinition);
3242
- if (edges.length === 1) {
3243
- filter[fieldKey] = {
3244
- eq: values[0]
3245
- };
3246
- } else if (edges.length > 1) {
3247
- filter[fieldKey] = {
3248
- in: values
3249
- };
3250
- } else {
3251
- filter[fieldKey] = {
3252
- eq: "___null___"
3253
- };
3254
- }
3255
- } else if (fieldDefinition.type === "object") {
3256
- if (fieldDefinition.templates) {
3257
- for (const templateName of Object.keys(filter[fieldKey])) {
3258
- const template = fieldDefinition.templates.find(
3259
- (template2) => !(typeof template2 === "string") && template2.name === templateName
3260
- );
3261
- if (template) {
3262
- await resolveReferences(
3263
- filter[fieldKey][templateName],
3264
- template.fields,
3265
- resolver
3266
- );
3267
- } else {
3268
- throw new Error(`Template ${templateName} not found`);
3269
- }
3270
- }
3271
- } else {
3272
- await resolveReferences(
3273
- filter[fieldKey],
3274
- fieldDefinition.fields,
3275
- resolver
3276
- );
3277
- }
3489
+ output[field?.name || key] = field?.type == "object" ? replaceNameOverrides(field, obj[key]) : obj[key];
3490
+ });
3491
+ return output;
3492
+ };
3493
+ var getTemplateForData = (field, data) => {
3494
+ if (field.templates?.length) {
3495
+ const templateKey = "_template";
3496
+ if (data[templateKey]) {
3497
+ const result = field.templates.find(
3498
+ (template) => template.nameOverride === data[templateKey] || template.name === data[templateKey]
3499
+ );
3500
+ if (result) {
3501
+ return result;
3278
3502
  }
3279
- } else {
3280
- throw new Error(`Unable to find field ${fieldKey}`);
3503
+ throw new Error(
3504
+ `Template "${data[templateKey]}" is not defined for field "${field.name}"`
3505
+ );
3281
3506
  }
3507
+ throw new Error(
3508
+ `Missing required key "${templateKey}" on field "${field.name}"`
3509
+ );
3510
+ } else {
3511
+ return field;
3282
3512
  }
3283
3513
  };
3284
- var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
3285
- for (const childFieldName of Object.keys(filterNode)) {
3286
- const childField = fields.find((field) => field.name === childFieldName);
3287
- if (!childField) {
3288
- throw new Error(`Unable to find type for field ${childFieldName}`);
3289
- }
3290
- collectConditionsForField(
3291
- childFieldName,
3292
- childField,
3293
- filterNode[childFieldName],
3294
- pathExpression,
3295
- collectCondition
3296
- );
3297
- }
3298
- };
3299
- var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3300
- if (field.list && field.templates) {
3301
- for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
3302
- const template = field.templates.find(
3303
- (template2) => !(typeof template2 === "string") && template2.name === filterKey
3304
- );
3305
- const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
3306
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
3307
- collectConditionsForChildFields(
3308
- childFilterNode,
3309
- template.fields,
3310
- filterPath,
3311
- collectCondition
3312
- );
3313
- }
3314
- } else {
3315
- const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
3316
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
3317
- collectConditionsForChildFields(
3318
- filterNode,
3319
- field.fields,
3320
- filterPath,
3321
- collectCondition
3322
- );
3323
- }
3324
- };
3325
- var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3326
- if (field.type === "object") {
3327
- collectConditionsForObjectField(
3328
- fieldName,
3329
- field,
3330
- filterNode,
3331
- pathExpression,
3332
- collectCondition
3333
- );
3334
- } else {
3335
- collectCondition({
3336
- filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
3337
- filterExpression: {
3338
- _type: field.type,
3339
- _list: !!field.list,
3340
- ...filterNode
3341
- }
3342
- });
3343
- }
3344
- };
3345
-
3346
- // src/resolver/media-utils.ts
3347
- var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
3348
- if (config && value) {
3349
- if (config.useRelativeMedia === true) {
3350
- return value;
3351
- }
3352
- if (hasTinaMediaConfig(schema) === true) {
3353
- const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
3354
- if (typeof value === "string" && value.includes(assetsURL)) {
3355
- const cleanMediaRoot = cleanUpSlashes(
3356
- schema.config.media.tina.mediaRoot
3357
- );
3358
- const strippedURL = value.replace(assetsURL, "");
3359
- return `${cleanMediaRoot}${strippedURL}`;
3360
- }
3361
- if (Array.isArray(value)) {
3362
- return value.map((v) => {
3363
- if (!v || typeof v !== "string")
3364
- return v;
3365
- const cleanMediaRoot = cleanUpSlashes(
3366
- schema.config.media.tina.mediaRoot
3367
- );
3368
- const strippedURL = v.replace(assetsURL, "");
3369
- return `${cleanMediaRoot}${strippedURL}`;
3370
- });
3371
- }
3372
- return value;
3373
- }
3374
- return value;
3375
- } else {
3376
- return value;
3377
- }
3378
- };
3379
- var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
3380
- if (config && value) {
3381
- if (config.useRelativeMedia === true) {
3382
- return value;
3383
- }
3384
- if (hasTinaMediaConfig(schema) === true) {
3385
- const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
3386
- if (typeof value === "string") {
3387
- const strippedValue = value.replace(cleanMediaRoot, "");
3388
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3389
- }
3390
- if (Array.isArray(value)) {
3391
- return value.map((v) => {
3392
- if (!v || typeof v !== "string")
3393
- return v;
3394
- const strippedValue = v.replace(cleanMediaRoot, "");
3395
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3396
- });
3397
- }
3398
- }
3399
- return value;
3400
- } else {
3401
- return value;
3402
- }
3403
- };
3404
- var cleanUpSlashes = (path7) => {
3405
- if (path7) {
3406
- return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
3407
- }
3408
- return "";
3409
- };
3410
- var hasTinaMediaConfig = (schema) => {
3411
- var _a, _b, _c, _d, _e, _f, _g, _h;
3412
- if (!((_b = (_a = schema.config) == null ? void 0 : _a.media) == null ? void 0 : _b.tina))
3413
- return false;
3414
- if (typeof ((_e = (_d = (_c = schema.config) == null ? void 0 : _c.media) == null ? void 0 : _d.tina) == null ? void 0 : _e.publicFolder) !== "string" && typeof ((_h = (_g = (_f = schema.config) == null ? void 0 : _f.media) == null ? void 0 : _g.tina) == null ? void 0 : _h.mediaRoot) !== "string")
3415
- return false;
3416
- return true;
3417
- };
3418
-
3419
- // src/resolver/index.ts
3420
- var import_graphql3 = require("graphql");
3421
-
3422
- // src/database/datalayer.ts
3423
- var import_jsonpath_plus = require("jsonpath-plus");
3424
- var import_js_sha1 = __toESM(require("js-sha1"));
3425
-
3426
- // src/database/level.ts
3427
- var ARRAY_ITEM_VALUE_SEPARATOR = ",";
3428
- var INDEX_KEY_FIELD_SEPARATOR = "";
3429
- var CONTENT_ROOT_PREFIX = "~";
3430
- var SUBLEVEL_OPTIONS = {
3431
- separator: INDEX_KEY_FIELD_SEPARATOR,
3432
- valueEncoding: "json"
3433
- };
3434
- var LevelProxyHandler = {
3435
- get: function(target, property) {
3436
- if (!target[property]) {
3437
- throw new Error(`The property, ${property.toString()}, doesn't exist`);
3438
- }
3439
- if (typeof target[property] !== "function") {
3440
- throw new Error(`The property, ${property.toString()}, is not a function`);
3441
- }
3442
- if (property === "get") {
3443
- return async (...args) => {
3444
- let result;
3445
- try {
3446
- result = await target[property].apply(target, args);
3447
- } catch (e) {
3448
- if (e.code !== "LEVEL_NOT_FOUND") {
3449
- throw e;
3450
- }
3451
- }
3452
- return result;
3453
- };
3454
- } else if (property === "sublevel") {
3455
- return (...args) => {
3456
- return new Proxy(
3457
- target[property].apply(target, args),
3458
- LevelProxyHandler
3459
- );
3460
- };
3461
- } else {
3462
- return (...args) => target[property].apply(target, args);
3463
- }
3464
- }
3465
- };
3466
- var LevelProxy = class {
3467
- constructor(level) {
3468
- return new Proxy(level, LevelProxyHandler);
3469
- }
3470
- };
3471
-
3472
- // src/database/datalayer.ts
3473
- var import_path2 = __toESM(require("path"));
3474
-
3475
- // src/database/util.ts
3476
- var import_toml = __toESM(require("@iarna/toml"));
3477
- var import_js_yaml = __toESM(require("js-yaml"));
3478
- var import_gray_matter = __toESM(require("gray-matter"));
3479
- var import_schema_tools3 = require("@tinacms/schema-tools");
3480
- var import_micromatch = __toESM(require("micromatch"));
3481
- var import_path = __toESM(require("path"));
3482
-
3483
- // src/database/alias-utils.ts
3484
- var replaceBlockAliases = (template, item) => {
3485
- const output = { ...item };
3486
- const templateKey = template.templateKey || "_template";
3487
- const templateName = output[templateKey];
3488
- const matchingTemplate = template.templates.find(
3489
- (t) => t.nameOverride == templateName || t.name == templateName
3490
- );
3491
- if (!matchingTemplate) {
3492
- throw new Error(
3493
- `Block template "${templateName}" is not defined for field "${template.name}"`
3494
- );
3495
- }
3496
- output._template = matchingTemplate.name;
3497
- if (templateKey != "_template") {
3498
- delete output[templateKey];
3499
- }
3500
- return output;
3501
- };
3502
- var replaceNameOverrides = (template, obj) => {
3503
- if (template.list) {
3504
- return obj.map((item) => {
3505
- if (isBlockField(template)) {
3506
- item = replaceBlockAliases(template, item);
3507
- }
3508
- return _replaceNameOverrides(
3509
- getTemplateForData(template, item).fields,
3510
- item
3511
- );
3512
- });
3513
- } else {
3514
- return _replaceNameOverrides(getTemplateForData(template, obj).fields, obj);
3515
- }
3516
- };
3517
- function isBlockField(field) {
3518
- var _a;
3519
- return field && field.type === "object" && ((_a = field.templates) == null ? void 0 : _a.length) > 0;
3520
- }
3521
- var _replaceNameOverrides = (fields, obj) => {
3522
- const output = {};
3523
- Object.keys(obj).forEach((key) => {
3524
- const field = fields.find(
3525
- (fieldWithMatchingAlias) => ((fieldWithMatchingAlias == null ? void 0 : fieldWithMatchingAlias.nameOverride) || (fieldWithMatchingAlias == null ? void 0 : fieldWithMatchingAlias.name)) === key
3526
- );
3527
- output[(field == null ? void 0 : field.name) || key] = (field == null ? void 0 : field.type) == "object" ? replaceNameOverrides(field, obj[key]) : obj[key];
3528
- });
3529
- return output;
3530
- };
3531
- var getTemplateForData = (field, data) => {
3532
- var _a;
3533
- if ((_a = field.templates) == null ? void 0 : _a.length) {
3534
- const templateKey = "_template";
3535
- if (data[templateKey]) {
3536
- const result = field.templates.find(
3537
- (template) => template.nameOverride === data[templateKey] || template.name === data[templateKey]
3538
- );
3539
- if (result) {
3540
- return result;
3541
- }
3542
- throw new Error(
3543
- `Template "${data[templateKey]}" is not defined for field "${field.name}"`
3544
- );
3545
- }
3546
- throw new Error(
3547
- `Missing required key "${templateKey}" on field "${field.name}"`
3548
- );
3549
- } else {
3550
- return field;
3551
- }
3552
- };
3553
- var applyBlockAliases = (template, item) => {
3554
- const output = { ...item };
3555
- const templateKey = template.templateKey || "_template";
3556
- const templateName = output._template;
3557
- const matchingTemplate = template.templates.find(
3558
- (t) => t.nameOverride == templateName || t.name == templateName
3559
- );
3560
- if (!matchingTemplate) {
3561
- throw new Error(
3562
- `Block template "${templateName}" is not defined for field "${template.name}"`
3514
+ var applyBlockAliases = (template, item) => {
3515
+ const output = { ...item };
3516
+ const templateKey = template.templateKey || "_template";
3517
+ const templateName = output._template;
3518
+ const matchingTemplate = template.templates.find(
3519
+ (t) => t.nameOverride == templateName || t.name == templateName
3520
+ );
3521
+ if (!matchingTemplate) {
3522
+ throw new Error(
3523
+ `Block template "${templateName}" is not defined for field "${template.name}"`
3563
3524
  );
3564
3525
  }
3565
3526
  output[templateKey] = matchingTemplate.nameOverride || matchingTemplate.name;
@@ -3588,8 +3549,8 @@ var _applyNameOverrides = (fields, obj) => {
3588
3549
  const output = {};
3589
3550
  Object.keys(obj).forEach((key) => {
3590
3551
  const field = fields.find((field2) => field2.name === key);
3591
- const outputKey = (field == null ? void 0 : field.nameOverride) || key;
3592
- output[outputKey] = (field == null ? void 0 : field.type) === "object" ? applyNameOverrides(field, obj[key]) : obj[key];
3552
+ const outputKey = field?.nameOverride || key;
3553
+ output[outputKey] = field?.type === "object" ? applyNameOverrides(field, obj[key]) : obj[key];
3593
3554
  });
3594
3555
  return output;
3595
3556
  };
@@ -3602,7 +3563,6 @@ var matterEngines = {
3602
3563
  }
3603
3564
  };
3604
3565
  var stringifyFile = (content, format, keepTemplateKey, markdownParseConfig) => {
3605
- var _a, _b;
3606
3566
  const {
3607
3567
  _relativePath,
3608
3568
  _keepTemplateKey,
@@ -3626,9 +3586,9 @@ var stringifyFile = (content, format, keepTemplateKey, markdownParseConfig) => {
3626
3586
  ${$_body}`,
3627
3587
  strippedContent,
3628
3588
  {
3629
- language: (_a = markdownParseConfig == null ? void 0 : markdownParseConfig.frontmatterFormat) != null ? _a : "yaml",
3589
+ language: markdownParseConfig?.frontmatterFormat ?? "yaml",
3630
3590
  engines: matterEngines,
3631
- delimiters: (_b = markdownParseConfig == null ? void 0 : markdownParseConfig.frontmatterDelimiters) != null ? _b : "---"
3591
+ delimiters: markdownParseConfig?.frontmatterDelimiters ?? "---"
3632
3592
  }
3633
3593
  );
3634
3594
  return ok;
@@ -3644,15 +3604,14 @@ ${$_body}`,
3644
3604
  }
3645
3605
  };
3646
3606
  var parseFile = (content, format, yupSchema, markdownParseConfig) => {
3647
- var _a, _b;
3648
3607
  try {
3649
3608
  switch (format) {
3650
3609
  case ".markdown":
3651
3610
  case ".mdx":
3652
3611
  case ".md":
3653
3612
  const contentJSON = (0, import_gray_matter.default)(content || "", {
3654
- language: (_a = markdownParseConfig == null ? void 0 : markdownParseConfig.frontmatterFormat) != null ? _a : "yaml",
3655
- delimiters: (_b = markdownParseConfig == null ? void 0 : markdownParseConfig.frontmatterDelimiters) != null ? _b : "---",
3613
+ language: markdownParseConfig?.frontmatterFormat ?? "yaml",
3614
+ delimiters: markdownParseConfig?.frontmatterDelimiters ?? "---",
3656
3615
  engines: matterEngines
3657
3616
  });
3658
3617
  const markdownData = {
@@ -3689,7 +3648,7 @@ var scanAllContent = async (tinaSchema, bridge, callback) => {
3689
3648
  const filesSeen = /* @__PURE__ */ new Map();
3690
3649
  const duplicateFiles = /* @__PURE__ */ new Set();
3691
3650
  await sequential(tinaSchema.getCollections(), async (collection) => {
3692
- const normalPath = (0, import_schema_tools3.normalizePath)(collection.path);
3651
+ const normalPath = (0, import_schema_tools4.normalizePath)(collection.path);
3693
3652
  const format = collection.format || "md";
3694
3653
  const documentPaths = await bridge.glob(normalPath, format);
3695
3654
  const matches = tinaSchema.getMatches({ collection });
@@ -3751,7 +3710,7 @@ var transformDocument = (filepath, contentObject, tinaSchema) => {
3751
3710
  ),
3752
3711
  template: void 0
3753
3712
  } : tinaSchema.getCollectionAndTemplateByFullPath(filepath, templateName);
3754
- const field = template == null ? void 0 : template.fields.find((field2) => {
3713
+ const field = template?.fields.find((field2) => {
3755
3714
  if (field2.type === "string" || field2.type === "rich-text") {
3756
3715
  if (field2.isBody) {
3757
3716
  return true;
@@ -3771,7 +3730,7 @@ var transformDocument = (filepath, contentObject, tinaSchema) => {
3771
3730
  ...data,
3772
3731
  _collection: collection.name,
3773
3732
  _keepTemplateKey: !!collection.templates,
3774
- _template: (template == null ? void 0 : template.namespace) ? lastItem(template == null ? void 0 : template.namespace) : void 0,
3733
+ _template: template?.namespace ? lastItem(template?.namespace) : void 0,
3775
3734
  _relativePath: filepath.replace(collection.path, "").replace(/^\/|\/$/g, ""),
3776
3735
  _id: filepath
3777
3736
  };
@@ -3780,10 +3739,10 @@ function hasOwnProperty(obj, prop) {
3780
3739
  return obj.hasOwnProperty(prop);
3781
3740
  }
3782
3741
  var getTemplateForFile = (templateInfo, data) => {
3783
- if ((templateInfo == null ? void 0 : templateInfo.type) === "object") {
3742
+ if (templateInfo?.type === "object") {
3784
3743
  return templateInfo.template;
3785
3744
  }
3786
- if ((templateInfo == null ? void 0 : templateInfo.type) === "union") {
3745
+ if (templateInfo?.type === "union") {
3787
3746
  if (hasOwnProperty(data, "_template")) {
3788
3747
  const template = templateInfo.templates.find(
3789
3748
  (t) => lastItem(t.namespace) === data._template
@@ -3801,14 +3760,14 @@ var getTemplateForFile = (templateInfo, data) => {
3801
3760
  throw new Error(`Unable to determine template`);
3802
3761
  };
3803
3762
  var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo) => {
3804
- const dataString = await bridge.get((0, import_schema_tools3.normalizePath)(filepath));
3763
+ const dataString = await bridge.get((0, import_schema_tools4.normalizePath)(filepath));
3805
3764
  const data = parseFile(
3806
3765
  dataString,
3807
3766
  import_path.default.extname(filepath),
3808
3767
  (yup3) => yup3.object({}),
3809
3768
  {
3810
- frontmatterDelimiters: collection == null ? void 0 : collection.frontmatterDelimiters,
3811
- frontmatterFormat: collection == null ? void 0 : collection.frontmatterFormat
3769
+ frontmatterDelimiters: collection?.frontmatterDelimiters,
3770
+ frontmatterFormat: collection?.frontmatterFormat
3812
3771
  }
3813
3772
  );
3814
3773
  const template = getTemplateForFile(templateInfo, data);
@@ -3823,6 +3782,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3823
3782
 
3824
3783
  // src/database/datalayer.ts
3825
3784
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
3785
+ var REFS_COLLECTIONS_SORT_KEY = "__refs__";
3786
+ var REFS_REFERENCE_FIELD = "__tina_ref__";
3787
+ var REFS_PATH_FIELD = "__tina_ref_path__";
3826
3788
  var DEFAULT_NUMERIC_LPAD = 4;
3827
3789
  var applyPadding = (input, pad) => {
3828
3790
  if (pad) {
@@ -4280,7 +4242,7 @@ var FolderTreeBuilder = class {
4280
4242
  return this._tree;
4281
4243
  }
4282
4244
  update(documentPath, collectionPath) {
4283
- let folderPath = import_path2.default.dirname((0, import_schema_tools3.normalizePath)(documentPath));
4245
+ let folderPath = import_path2.default.dirname((0, import_schema_tools4.normalizePath)(documentPath));
4284
4246
  if (folderPath === ".") {
4285
4247
  folderPath = "";
4286
4248
  }
@@ -4293,7 +4255,7 @@ var FolderTreeBuilder = class {
4293
4255
  if (!this._tree[current2]) {
4294
4256
  this._tree[current2] = /* @__PURE__ */ new Set();
4295
4257
  }
4296
- this._tree[current2].add((0, import_schema_tools3.normalizePath)(import_path2.default.join(current2, part)));
4258
+ this._tree[current2].add((0, import_schema_tools4.normalizePath)(import_path2.default.join(current2, part)));
4297
4259
  parent.push(part);
4298
4260
  });
4299
4261
  const current = parent.join("/");
@@ -4332,6 +4294,7 @@ var makeFolderOpsForCollection = (folderTree, collection, indexDefinitions, opTy
4332
4294
  result.push({
4333
4295
  type: opType,
4334
4296
  key: `${collection.path}/${subFolderKey}.${collection.format}`,
4297
+ // replace the root with the collection path
4335
4298
  sublevel: indexSublevel,
4336
4299
  value: {}
4337
4300
  });
@@ -4395,6 +4358,57 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4395
4358
  }
4396
4359
  return result;
4397
4360
  };
4361
+ var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4362
+ const result = [];
4363
+ if (collection) {
4364
+ for (const [c, referencePaths] of Object.entries(references || {})) {
4365
+ if (!referencePaths.length) {
4366
+ continue;
4367
+ }
4368
+ const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4369
+ const refSublevel = collectionSublevel.sublevel(
4370
+ REFS_COLLECTIONS_SORT_KEY,
4371
+ SUBLEVEL_OPTIONS
4372
+ );
4373
+ const references2 = {};
4374
+ for (const path7 of referencePaths) {
4375
+ const ref = (0, import_jsonpath_plus.JSONPath)({ path: path7, json: data });
4376
+ if (!ref) {
4377
+ continue;
4378
+ }
4379
+ if (Array.isArray(ref)) {
4380
+ for (const r of ref) {
4381
+ if (!r) {
4382
+ continue;
4383
+ }
4384
+ if (references2[r]) {
4385
+ references2[r].push(path7);
4386
+ } else {
4387
+ references2[r] = [path7];
4388
+ }
4389
+ }
4390
+ } else {
4391
+ if (references2[ref]) {
4392
+ references2[ref].push(path7);
4393
+ } else {
4394
+ references2[ref] = [path7];
4395
+ }
4396
+ }
4397
+ }
4398
+ for (const ref of Object.keys(references2)) {
4399
+ for (const path7 of references2[ref]) {
4400
+ result.push({
4401
+ type: opType,
4402
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4403
+ sublevel: refSublevel,
4404
+ value: opType === "put" ? {} : void 0
4405
+ });
4406
+ }
4407
+ }
4408
+ }
4409
+ }
4410
+ return result;
4411
+ };
4398
4412
  var makeStringEscaper = (regex, replacement) => {
4399
4413
  return (input) => {
4400
4414
  if (Array.isArray(input)) {
@@ -4408,19 +4422,254 @@ var makeStringEscaper = (regex, replacement) => {
4408
4422
  return input;
4409
4423
  }
4410
4424
  }
4411
- };
4425
+ };
4426
+ };
4427
+ var stringEscaper = makeStringEscaper(
4428
+ new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4429
+ encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4430
+ );
4431
+
4432
+ // src/resolver/error.ts
4433
+ var TinaGraphQLError = class extends Error {
4434
+ constructor(message, extensions) {
4435
+ super(message);
4436
+ if (!this.name) {
4437
+ Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
4438
+ }
4439
+ this.extensions = { ...extensions };
4440
+ }
4441
+ };
4442
+ var TinaFetchError = class extends Error {
4443
+ constructor(message, args) {
4444
+ super(message);
4445
+ this.name = "TinaFetchError";
4446
+ this.collection = args.collection;
4447
+ this.file = args.file;
4448
+ this.originalError = args.originalError;
4449
+ }
4450
+ };
4451
+ var TinaQueryError = class extends TinaFetchError {
4452
+ constructor(args) {
4453
+ super(
4454
+ `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4455
+ args
4456
+ );
4457
+ }
4458
+ };
4459
+ var TinaParseDocumentError = class extends TinaFetchError {
4460
+ constructor(args) {
4461
+ super(
4462
+ `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4463
+ args
4464
+ );
4465
+ }
4466
+ toString() {
4467
+ return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
4468
+ }
4469
+ };
4470
+ var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
4471
+ var handleFetchErrorError = (e, verbose) => {
4472
+ if (e instanceof Error) {
4473
+ if (e instanceof TinaFetchError) {
4474
+ if (verbose) {
4475
+ console.log(e.toString());
4476
+ console.log(e);
4477
+ console.log(e.stack);
4478
+ }
4479
+ }
4480
+ } else {
4481
+ console.error(e);
4482
+ }
4483
+ throw e;
4484
+ };
4485
+
4486
+ // src/resolver/filter-utils.ts
4487
+ var resolveReferences = async (filter, fields, resolver) => {
4488
+ for (const fieldKey of Object.keys(filter)) {
4489
+ const fieldDefinition = fields.find(
4490
+ (f) => f.name === fieldKey
4491
+ );
4492
+ if (fieldDefinition) {
4493
+ if (fieldDefinition.type === "reference") {
4494
+ const { edges, values } = await resolver(filter, fieldDefinition);
4495
+ if (edges.length === 1) {
4496
+ filter[fieldKey] = {
4497
+ eq: values[0]
4498
+ };
4499
+ } else if (edges.length > 1) {
4500
+ filter[fieldKey] = {
4501
+ in: values
4502
+ };
4503
+ } else {
4504
+ filter[fieldKey] = {
4505
+ eq: "___null___"
4506
+ };
4507
+ }
4508
+ } else if (fieldDefinition.type === "object") {
4509
+ if (fieldDefinition.templates) {
4510
+ for (const templateName of Object.keys(filter[fieldKey])) {
4511
+ const template = fieldDefinition.templates.find(
4512
+ (template2) => !(typeof template2 === "string") && template2.name === templateName
4513
+ );
4514
+ if (template) {
4515
+ await resolveReferences(
4516
+ filter[fieldKey][templateName],
4517
+ template.fields,
4518
+ resolver
4519
+ );
4520
+ } else {
4521
+ throw new Error(`Template ${templateName} not found`);
4522
+ }
4523
+ }
4524
+ } else {
4525
+ await resolveReferences(
4526
+ filter[fieldKey],
4527
+ fieldDefinition.fields,
4528
+ resolver
4529
+ );
4530
+ }
4531
+ }
4532
+ } else {
4533
+ throw new Error(`Unable to find field ${fieldKey}`);
4534
+ }
4535
+ }
4536
+ };
4537
+ var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
4538
+ for (const childFieldName of Object.keys(filterNode)) {
4539
+ const childField = fields.find((field) => field.name === childFieldName);
4540
+ if (!childField) {
4541
+ throw new Error(`Unable to find type for field ${childFieldName}`);
4542
+ }
4543
+ collectConditionsForField(
4544
+ childFieldName,
4545
+ childField,
4546
+ filterNode[childFieldName],
4547
+ pathExpression,
4548
+ collectCondition
4549
+ );
4550
+ }
4551
+ };
4552
+ var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4553
+ if (field.list && field.templates) {
4554
+ for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
4555
+ const template = field.templates.find(
4556
+ (template2) => !(typeof template2 === "string") && template2.name === filterKey
4557
+ );
4558
+ const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
4559
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
4560
+ collectConditionsForChildFields(
4561
+ childFilterNode,
4562
+ template.fields,
4563
+ filterPath,
4564
+ collectCondition
4565
+ );
4566
+ }
4567
+ } else {
4568
+ const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
4569
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
4570
+ collectConditionsForChildFields(
4571
+ filterNode,
4572
+ field.fields,
4573
+ filterPath,
4574
+ collectCondition
4575
+ );
4576
+ }
4577
+ };
4578
+ var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4579
+ if (field.type === "object") {
4580
+ collectConditionsForObjectField(
4581
+ fieldName,
4582
+ field,
4583
+ filterNode,
4584
+ pathExpression,
4585
+ collectCondition
4586
+ );
4587
+ } else {
4588
+ collectCondition({
4589
+ filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
4590
+ filterExpression: {
4591
+ _type: field.type,
4592
+ _list: !!field.list,
4593
+ ...filterNode
4594
+ }
4595
+ });
4596
+ }
4597
+ };
4598
+
4599
+ // src/resolver/media-utils.ts
4600
+ var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
4601
+ if (config && value) {
4602
+ if (config.useRelativeMedia === true) {
4603
+ return value;
4604
+ }
4605
+ if (hasTinaMediaConfig(schema) === true) {
4606
+ const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
4607
+ if (typeof value === "string" && value.includes(assetsURL)) {
4608
+ const cleanMediaRoot = cleanUpSlashes(
4609
+ schema.config.media.tina.mediaRoot
4610
+ );
4611
+ const strippedURL = value.replace(assetsURL, "");
4612
+ return `${cleanMediaRoot}${strippedURL}`;
4613
+ }
4614
+ if (Array.isArray(value)) {
4615
+ return value.map((v) => {
4616
+ if (!v || typeof v !== "string") return v;
4617
+ const cleanMediaRoot = cleanUpSlashes(
4618
+ schema.config.media.tina.mediaRoot
4619
+ );
4620
+ const strippedURL = v.replace(assetsURL, "");
4621
+ return `${cleanMediaRoot}${strippedURL}`;
4622
+ });
4623
+ }
4624
+ return value;
4625
+ }
4626
+ return value;
4627
+ } else {
4628
+ return value;
4629
+ }
4630
+ };
4631
+ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
4632
+ if (config && value) {
4633
+ if (config.useRelativeMedia === true) {
4634
+ return value;
4635
+ }
4636
+ if (hasTinaMediaConfig(schema) === true) {
4637
+ const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
4638
+ if (typeof value === "string") {
4639
+ const strippedValue = value.replace(cleanMediaRoot, "");
4640
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4641
+ }
4642
+ if (Array.isArray(value)) {
4643
+ return value.map((v) => {
4644
+ if (!v || typeof v !== "string") return v;
4645
+ const strippedValue = v.replace(cleanMediaRoot, "");
4646
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4647
+ });
4648
+ }
4649
+ }
4650
+ return value;
4651
+ } else {
4652
+ return value;
4653
+ }
4654
+ };
4655
+ var cleanUpSlashes = (path7) => {
4656
+ if (path7) {
4657
+ return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
4658
+ }
4659
+ return "";
4660
+ };
4661
+ var hasTinaMediaConfig = (schema) => {
4662
+ if (!schema.config?.media?.tina) return false;
4663
+ if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
4664
+ return false;
4665
+ return true;
4412
4666
  };
4413
- var stringEscaper = makeStringEscaper(
4414
- new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4415
- encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4416
- );
4417
4667
 
4418
4668
  // src/resolver/index.ts
4419
4669
  var createResolver = (args) => {
4420
4670
  return new Resolver(args);
4421
4671
  };
4422
4672
  var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tinaSchema, config, isAudit) => {
4423
- var _a, _b;
4424
4673
  if (!rawData) {
4425
4674
  return void 0;
4426
4675
  }
@@ -4447,7 +4696,8 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4447
4696
  case "password":
4448
4697
  accumulator[field.name] = {
4449
4698
  value: void 0,
4450
- passwordChangeRequired: (_a = value["passwordChangeRequired"]) != null ? _a : false
4699
+ // never resolve the password hash
4700
+ passwordChangeRequired: value["passwordChangeRequired"] ?? false
4451
4701
  };
4452
4702
  break;
4453
4703
  case "image":
@@ -4463,11 +4713,11 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4463
4713
  field,
4464
4714
  (value2) => resolveMediaRelativeToCloud(value2, config, tinaSchema.schema)
4465
4715
  );
4466
- if (((_b = tree == null ? void 0 : tree.children[0]) == null ? void 0 : _b.type) === "invalid_markdown") {
4716
+ if (tree?.children[0]?.type === "invalid_markdown") {
4467
4717
  if (isAudit) {
4468
- const invalidNode = tree == null ? void 0 : tree.children[0];
4718
+ const invalidNode = tree?.children[0];
4469
4719
  throw new import_graphql3.GraphQLError(
4470
- `${invalidNode == null ? void 0 : invalidNode.message}${invalidNode.position ? ` at line ${invalidNode.position.start.line}, column ${invalidNode.position.start.column}` : ""}`
4720
+ `${invalidNode?.message}${invalidNode.position ? ` at line ${invalidNode.position.start.line}, column ${invalidNode.position.start.column}` : ""}`
4471
4721
  );
4472
4722
  }
4473
4723
  }
@@ -4541,7 +4791,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4541
4791
  }
4542
4792
  return accumulator;
4543
4793
  };
4544
- var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit) => {
4794
+ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit, hasReferences) => {
4545
4795
  const collection = tinaSchema.getCollection(rawData._collection);
4546
4796
  try {
4547
4797
  const template = tinaSchema.getTemplateForData({
@@ -4575,16 +4825,15 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4575
4825
  originalError: e,
4576
4826
  collection: collection.name,
4577
4827
  includeAuditMessage: !isAudit,
4578
- file: relativePath,
4579
- stack: e.stack
4828
+ file: relativePath
4580
4829
  });
4581
4830
  }
4582
4831
  const titleField = template.fields.find((x) => {
4583
- if (x.type === "string" && (x == null ? void 0 : x.isTitle)) {
4832
+ if (x.type === "string" && x?.isTitle) {
4584
4833
  return true;
4585
4834
  }
4586
4835
  });
4587
- const titleFieldName = titleField == null ? void 0 : titleField.name;
4836
+ const titleFieldName = titleField?.name;
4588
4837
  const title = data[titleFieldName || " "] || null;
4589
4838
  return {
4590
4839
  __typename: collection.fields ? NAMER.documentTypeName(collection.namespace) : NAMER.documentTypeName(template.namespace),
@@ -4595,6 +4844,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4595
4844
  basename,
4596
4845
  filename,
4597
4846
  extension,
4847
+ hasReferences,
4598
4848
  path: fullPath,
4599
4849
  relativePath,
4600
4850
  breadcrumbs,
@@ -4614,6 +4864,34 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4614
4864
  throw e;
4615
4865
  }
4616
4866
  };
4867
+ var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4868
+ let updated = false;
4869
+ if (!path7.includes(".") && !path7.includes("[")) {
4870
+ if (path7 in obj && obj[path7] === oldValue) {
4871
+ obj[path7] = newValue;
4872
+ updated = true;
4873
+ }
4874
+ return { object: obj, updated };
4875
+ }
4876
+ const parentPath = path7.replace(/\.[^.\[\]]+$/, "");
4877
+ const keyToUpdate = path7.match(/[^.\[\]]+$/)[0];
4878
+ const parents = (0, import_jsonpath_plus2.JSONPath)({
4879
+ path: parentPath,
4880
+ json: obj,
4881
+ resultType: "value"
4882
+ });
4883
+ if (parents.length > 0) {
4884
+ parents.forEach((parent) => {
4885
+ if (parent && typeof parent === "object" && keyToUpdate in parent) {
4886
+ if (parent[keyToUpdate] === oldValue) {
4887
+ parent[keyToUpdate] = newValue;
4888
+ updated = true;
4889
+ }
4890
+ }
4891
+ });
4892
+ }
4893
+ return { object: obj, updated };
4894
+ };
4617
4895
  var Resolver = class {
4618
4896
  constructor(init) {
4619
4897
  this.init = init;
@@ -4621,6 +4899,7 @@ var Resolver = class {
4621
4899
  const collection = this.tinaSchema.getCollection(collectionName);
4622
4900
  const extraFields = {};
4623
4901
  return {
4902
+ // return the collection and hasDocuments to resolve documents at a lower level
4624
4903
  documents: { collection, hasDocuments },
4625
4904
  ...collection,
4626
4905
  ...extraFields
@@ -4628,7 +4907,9 @@ var Resolver = class {
4628
4907
  };
4629
4908
  this.getRaw = async (fullPath) => {
4630
4909
  if (typeof fullPath !== "string") {
4631
- throw new Error(`fullPath must be of type string for getDocument request`);
4910
+ throw new Error(
4911
+ `fullPath must be of type string for getDocument request`
4912
+ );
4632
4913
  }
4633
4914
  return this.database.get(fullPath);
4634
4915
  };
@@ -4655,22 +4936,28 @@ var Resolver = class {
4655
4936
  );
4656
4937
  }
4657
4938
  };
4658
- this.getDocument = async (fullPath) => {
4939
+ this.getDocument = async (fullPath, opts = {}) => {
4659
4940
  if (typeof fullPath !== "string") {
4660
- throw new Error(`fullPath must be of type string for getDocument request`);
4941
+ throw new Error(
4942
+ `fullPath must be of type string for getDocument request`
4943
+ );
4661
4944
  }
4662
4945
  const rawData = await this.getRaw(fullPath);
4946
+ const hasReferences = opts?.checkReferences ? await this.hasReferences(fullPath, opts.collection) : void 0;
4663
4947
  return transformDocumentIntoPayload(
4664
4948
  fullPath,
4665
4949
  rawData,
4666
4950
  this.tinaSchema,
4667
4951
  this.config,
4668
- this.isAudit
4952
+ this.isAudit,
4953
+ hasReferences
4669
4954
  );
4670
4955
  };
4671
4956
  this.deleteDocument = async (fullPath) => {
4672
4957
  if (typeof fullPath !== "string") {
4673
- throw new Error(`fullPath must be of type string for getDocument request`);
4958
+ throw new Error(
4959
+ `fullPath must be of type string for getDocument request`
4960
+ );
4674
4961
  }
4675
4962
  await this.database.delete(fullPath);
4676
4963
  };
@@ -4696,16 +4983,18 @@ var Resolver = class {
4696
4983
  return this.buildFieldMutations(
4697
4984
  item,
4698
4985
  objectTemplate,
4699
- idField && existingData && (existingData == null ? void 0 : existingData.find(
4986
+ idField && existingData && existingData?.find(
4700
4987
  (d) => d[idField.name] === item[idField.name]
4701
- ))
4988
+ )
4702
4989
  );
4703
4990
  }
4704
4991
  )
4705
4992
  );
4706
4993
  } else {
4707
4994
  return this.buildFieldMutations(
4995
+ // @ts-ignore FIXME Argument of type 'string | object' is not assignable to parameter of type '{ [fieldName: string]: string | object | (string | object)[]; }'
4708
4996
  fieldValue,
4997
+ //@ts-ignore
4709
4998
  objectTemplate,
4710
4999
  existingData
4711
5000
  );
@@ -4717,6 +5006,7 @@ var Resolver = class {
4717
5006
  fieldValue.map(async (item) => {
4718
5007
  if (typeof item === "string") {
4719
5008
  throw new Error(
5009
+ //@ts-ignore
4720
5010
  `Expected object for template value for field ${field.name}`
4721
5011
  );
4722
5012
  }
@@ -4725,16 +5015,19 @@ var Resolver = class {
4725
5015
  });
4726
5016
  const [templateName] = Object.entries(item)[0];
4727
5017
  const template = templates.find(
5018
+ //@ts-ignore
4728
5019
  (template2) => template2.name === templateName
4729
5020
  );
4730
5021
  if (!template) {
4731
5022
  throw new Error(`Expected to find template ${templateName}`);
4732
5023
  }
4733
5024
  return {
5025
+ // @ts-ignore FIXME Argument of type 'unknown' is not assignable to parameter of type '{ [fieldName: string]: string | { [key: string]: unknown; } | (string | { [key: string]: unknown; })[]; }'
4734
5026
  ...await this.buildFieldMutations(
4735
5027
  item[template.name],
4736
5028
  template
4737
5029
  ),
5030
+ //@ts-ignore
4738
5031
  _template: template.name
4739
5032
  };
4740
5033
  })
@@ -4742,6 +5035,7 @@ var Resolver = class {
4742
5035
  } else {
4743
5036
  if (typeof fieldValue === "string") {
4744
5037
  throw new Error(
5038
+ //@ts-ignore
4745
5039
  `Expected object for template value for field ${field.name}`
4746
5040
  );
4747
5041
  }
@@ -4750,16 +5044,19 @@ var Resolver = class {
4750
5044
  });
4751
5045
  const [templateName] = Object.entries(fieldValue)[0];
4752
5046
  const template = templates.find(
5047
+ //@ts-ignore
4753
5048
  (template2) => template2.name === templateName
4754
5049
  );
4755
5050
  if (!template) {
4756
5051
  throw new Error(`Expected to find template ${templateName}`);
4757
5052
  }
4758
5053
  return {
5054
+ // @ts-ignore FIXME Argument of type 'unknown' is not assignable to parameter of type '{ [fieldName: string]: string | { [key: string]: unknown; } | (string | { [key: string]: unknown; })[]; }'
4759
5055
  ...await this.buildFieldMutations(
4760
5056
  fieldValue[template.name],
4761
5057
  template
4762
5058
  ),
5059
+ //@ts-ignore
4763
5060
  _template: template.name
4764
5061
  };
4765
5062
  }
@@ -4799,6 +5096,7 @@ var Resolver = class {
4799
5096
  return this.getDocument(realPath);
4800
5097
  }
4801
5098
  const params = await this.buildObjectMutations(
5099
+ // @ts-ignore
4802
5100
  args.params[collection.name],
4803
5101
  collection
4804
5102
  );
@@ -4813,7 +5111,7 @@ var Resolver = class {
4813
5111
  isCollectionSpecific
4814
5112
  }) => {
4815
5113
  const doc = await this.getDocument(realPath);
4816
- const oldDoc = this.resolveLegacyValues((doc == null ? void 0 : doc._rawData) || {}, collection);
5114
+ const oldDoc = this.resolveLegacyValues(doc?._rawData || {}, collection);
4817
5115
  if (isAddPendingDocument === true) {
4818
5116
  const templateInfo = this.tinaSchema.getTemplatesForCollectable(collection);
4819
5117
  const params2 = this.buildParams(args);
@@ -4823,7 +5121,7 @@ var Resolver = class {
4823
5121
  const values = await this.buildFieldMutations(
4824
5122
  params2,
4825
5123
  templateInfo.template,
4826
- doc == null ? void 0 : doc._rawData
5124
+ doc?._rawData
4827
5125
  );
4828
5126
  await this.database.put(
4829
5127
  realPath,
@@ -4844,9 +5142,10 @@ var Resolver = class {
4844
5142
  const values = {
4845
5143
  ...oldDoc,
4846
5144
  ...await this.buildFieldMutations(
5145
+ // @ts-ignore FIXME: failing on unknown, which we don't need to know because it's recursive
4847
5146
  templateParams,
4848
5147
  template,
4849
- doc == null ? void 0 : doc._rawData
5148
+ doc?._rawData
4850
5149
  ),
4851
5150
  _template: lastItem(template.namespace)
4852
5151
  };
@@ -4857,17 +5156,25 @@ var Resolver = class {
4857
5156
  return this.getDocument(realPath);
4858
5157
  }
4859
5158
  const params = await this.buildObjectMutations(
5159
+ //@ts-ignore
4860
5160
  isCollectionSpecific ? args.params : args.params[collection.name],
4861
5161
  collection,
4862
- doc == null ? void 0 : doc._rawData
5162
+ doc?._rawData
5163
+ );
5164
+ await this.database.put(
5165
+ realPath,
5166
+ { ...oldDoc, ...params },
5167
+ collection.name
4863
5168
  );
4864
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
4865
5169
  return this.getDocument(realPath);
4866
5170
  };
5171
+ /**
5172
+ * Returns top-level fields which are not defined in the collection, so their
5173
+ * values are not eliminated from Tina when new values are saved
5174
+ */
4867
5175
  this.resolveLegacyValues = (oldDoc, collection) => {
4868
5176
  const legacyValues = {};
4869
5177
  Object.entries(oldDoc).forEach(([key, value]) => {
4870
- var _a;
4871
5178
  const reservedKeys = [
4872
5179
  "$_body",
4873
5180
  "_collection",
@@ -4880,7 +5187,7 @@ var Resolver = class {
4880
5187
  return;
4881
5188
  }
4882
5189
  if (oldDoc._template && collection.templates) {
4883
- const template = (_a = collection.templates) == null ? void 0 : _a.find(
5190
+ const template = collection.templates?.find(
4884
5191
  ({ name }) => name === oldDoc._template
4885
5192
  );
4886
5193
  if (template) {
@@ -4927,7 +5234,7 @@ var Resolver = class {
4927
5234
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
4928
5235
  );
4929
5236
  const collection = await this.tinaSchema.getCollection(collectionLookup);
4930
- let realPath = import_path3.default.join(collection == null ? void 0 : collection.path, args.relativePath);
5237
+ let realPath = import_path3.default.join(collection?.path, args.relativePath);
4931
5238
  if (isFolderCreation) {
4932
5239
  realPath = `${realPath}/.gitkeep.${collection.format || "md"}`;
4933
5240
  }
@@ -4969,6 +5276,40 @@ var Resolver = class {
4969
5276
  if (isDeletion) {
4970
5277
  const doc = await this.getDocument(realPath);
4971
5278
  await this.deleteDocument(realPath);
5279
+ if (await this.hasReferences(realPath, collection)) {
5280
+ const collRefs = await this.findReferences(realPath, collection);
5281
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5282
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5283
+ docsWithRefs
5284
+ )) {
5285
+ let refDoc = await this.getRaw(pathToDocWithRef);
5286
+ let hasUpdate = false;
5287
+ for (const path7 of referencePaths) {
5288
+ const { object: object2, updated } = updateObjectWithJsonPath(
5289
+ refDoc,
5290
+ path7,
5291
+ realPath,
5292
+ null
5293
+ );
5294
+ refDoc = object2;
5295
+ hasUpdate = updated || hasUpdate;
5296
+ }
5297
+ if (hasUpdate) {
5298
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5299
+ if (!collectionWithRef) {
5300
+ throw new Error(
5301
+ `Unable to find collection for ${pathToDocWithRef}`
5302
+ );
5303
+ }
5304
+ await this.database.put(
5305
+ pathToDocWithRef,
5306
+ refDoc,
5307
+ collectionWithRef.name
5308
+ );
5309
+ }
5310
+ }
5311
+ }
5312
+ }
4972
5313
  return doc;
4973
5314
  }
4974
5315
  if (isUpdateName) {
@@ -4977,20 +5318,57 @@ var Resolver = class {
4977
5318
  (yup3) => yup3.object({ params: yup3.object().required() })
4978
5319
  );
4979
5320
  assertShape(
4980
- args == null ? void 0 : args.params,
5321
+ args?.params,
4981
5322
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
4982
5323
  );
4983
5324
  const doc = await this.getDocument(realPath);
4984
5325
  const newRealPath = import_path3.default.join(
4985
- collection == null ? void 0 : collection.path,
5326
+ collection?.path,
4986
5327
  args.params.relativePath
4987
5328
  );
5329
+ if (newRealPath === realPath) {
5330
+ return doc;
5331
+ }
4988
5332
  await this.database.put(newRealPath, doc._rawData, collection.name);
4989
5333
  await this.deleteDocument(realPath);
5334
+ const collRefs = await this.findReferences(realPath, collection);
5335
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5336
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5337
+ docsWithRefs
5338
+ )) {
5339
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5340
+ let hasUpdate = false;
5341
+ for (const path7 of referencePaths) {
5342
+ const { object: object2, updated } = updateObjectWithJsonPath(
5343
+ docWithRef,
5344
+ path7,
5345
+ realPath,
5346
+ newRealPath
5347
+ );
5348
+ docWithRef = object2;
5349
+ hasUpdate = updated || hasUpdate;
5350
+ }
5351
+ if (hasUpdate) {
5352
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5353
+ if (!collectionWithRef) {
5354
+ throw new Error(
5355
+ `Unable to find collection for ${pathToDocWithRef}`
5356
+ );
5357
+ }
5358
+ await this.database.put(
5359
+ pathToDocWithRef,
5360
+ docWithRef,
5361
+ collectionWithRef.name
5362
+ );
5363
+ }
5364
+ }
5365
+ }
4990
5366
  return this.getDocument(newRealPath);
4991
5367
  }
4992
5368
  if (alreadyExists === false) {
4993
- throw new Error(`Unable to update document, ${realPath} does not exist`);
5369
+ throw new Error(
5370
+ `Unable to update document, ${realPath} does not exist`
5371
+ );
4994
5372
  }
4995
5373
  return this.updateResolveDocument({
4996
5374
  collection,
@@ -5000,7 +5378,10 @@ var Resolver = class {
5000
5378
  isCollectionSpecific
5001
5379
  });
5002
5380
  } else {
5003
- return this.getDocument(realPath);
5381
+ return this.getDocument(realPath, {
5382
+ collection,
5383
+ checkReferences: true
5384
+ });
5004
5385
  }
5005
5386
  };
5006
5387
  this.resolveCollectionConnections = async ({ ids }) => {
@@ -5037,6 +5418,7 @@ var Resolver = class {
5037
5418
  },
5038
5419
  collection: referencedCollection,
5039
5420
  hydrator: (path7) => path7
5421
+ // just return the path
5040
5422
  }
5041
5423
  );
5042
5424
  const { edges } = resolvedCollectionConnection;
@@ -5104,8 +5486,83 @@ var Resolver = class {
5104
5486
  }
5105
5487
  };
5106
5488
  };
5489
+ /**
5490
+ * Checks if a document has references to it
5491
+ * @param id The id of the document to check for references
5492
+ * @param c The collection to check for references
5493
+ * @returns true if the document has references, false otherwise
5494
+ */
5495
+ this.hasReferences = async (id, c) => {
5496
+ let count = 0;
5497
+ await this.database.query(
5498
+ {
5499
+ collection: c.name,
5500
+ filterChain: makeFilterChain({
5501
+ conditions: [
5502
+ {
5503
+ filterPath: REFS_REFERENCE_FIELD,
5504
+ filterExpression: {
5505
+ _type: "string",
5506
+ _list: false,
5507
+ eq: id
5508
+ }
5509
+ }
5510
+ ]
5511
+ }),
5512
+ sort: REFS_COLLECTIONS_SORT_KEY
5513
+ },
5514
+ (refId) => {
5515
+ count++;
5516
+ return refId;
5517
+ }
5518
+ );
5519
+ if (count) {
5520
+ return true;
5521
+ }
5522
+ return false;
5523
+ };
5524
+ /**
5525
+ * Finds references to a document
5526
+ * @param id the id of the document to find references to
5527
+ * @param c the collection to find references in
5528
+ * @returns a map of references to the document
5529
+ */
5530
+ this.findReferences = async (id, c) => {
5531
+ const references = {};
5532
+ await this.database.query(
5533
+ {
5534
+ collection: c.name,
5535
+ filterChain: makeFilterChain({
5536
+ conditions: [
5537
+ {
5538
+ filterPath: REFS_REFERENCE_FIELD,
5539
+ filterExpression: {
5540
+ _type: "string",
5541
+ _list: false,
5542
+ eq: id
5543
+ }
5544
+ }
5545
+ ]
5546
+ }),
5547
+ sort: REFS_COLLECTIONS_SORT_KEY
5548
+ },
5549
+ (refId, rawItem) => {
5550
+ if (!references[c.name]) {
5551
+ references[c.name] = {};
5552
+ }
5553
+ if (!references[c.name][refId]) {
5554
+ references[c.name][refId] = [];
5555
+ }
5556
+ const referencePath = rawItem?.[REFS_PATH_FIELD];
5557
+ if (referencePath) {
5558
+ references[c.name][refId].push(referencePath);
5559
+ }
5560
+ return refId;
5561
+ }
5562
+ );
5563
+ return references;
5564
+ };
5107
5565
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
5108
- var _a;
5109
5566
  const accum = {};
5110
5567
  for (const passwordField of template.fields.filter(
5111
5568
  (f) => f.type === "password"
@@ -5148,7 +5605,7 @@ var Resolver = class {
5148
5605
  accum[fieldName] = await this.buildObjectMutations(
5149
5606
  fieldValue,
5150
5607
  field,
5151
- existingData == null ? void 0 : existingData[fieldName]
5608
+ existingData?.[fieldName]
5152
5609
  );
5153
5610
  break;
5154
5611
  case "password":
@@ -5167,12 +5624,12 @@ var Resolver = class {
5167
5624
  } else {
5168
5625
  accum[fieldName] = {
5169
5626
  ...fieldValue,
5170
- value: (_a = existingData == null ? void 0 : existingData[fieldName]) == null ? void 0 : _a["value"]
5627
+ value: existingData?.[fieldName]?.["value"]
5171
5628
  };
5172
5629
  }
5173
5630
  break;
5174
5631
  case "rich-text":
5175
- accum[fieldName] = (0, import_mdx.stringifyMDX)(
5632
+ accum[fieldName] = (0, import_mdx.serializeMDX)(
5176
5633
  fieldValue,
5177
5634
  field,
5178
5635
  (fieldValue2) => resolveMediaCloudToRelative(
@@ -5191,6 +5648,27 @@ var Resolver = class {
5191
5648
  }
5192
5649
  return accum;
5193
5650
  };
5651
+ /**
5652
+ * A mutation looks nearly identical between updateDocument:
5653
+ * ```graphql
5654
+ * updateDocument(collection: $collection,relativePath: $path, params: {
5655
+ * post: {
5656
+ * title: "Hello, World"
5657
+ * }
5658
+ * })`
5659
+ * ```
5660
+ * and `updatePostDocument`:
5661
+ * ```graphql
5662
+ * updatePostDocument(relativePath: $path, params: {
5663
+ * title: "Hello, World"
5664
+ * })
5665
+ * ```
5666
+ * The problem here is that we don't know whether the payload came from `updateDocument`
5667
+ * or `updatePostDocument` (we could, but for now it's easier not to pipe those details through),
5668
+ * But we do know that when given a `args.collection` value, we can assume that
5669
+ * this was a `updateDocument` request, and thus - should grab the data
5670
+ * from the corresponding field name in the key
5671
+ */
5194
5672
  this.buildParams = (args) => {
5195
5673
  try {
5196
5674
  assertShape(
@@ -5258,8 +5736,129 @@ var resolveDateInput = (value) => {
5258
5736
  return date;
5259
5737
  };
5260
5738
 
5261
- // src/resolve.ts
5262
- var import_lodash4 = __toESM(require("lodash.set"));
5739
+ // src/resolver/auth-fields.ts
5740
+ var import_compat = require("es-toolkit/compat");
5741
+ async function getUserDocumentContext(tinaSchema, resolver) {
5742
+ const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5743
+ if (!collection) {
5744
+ throw new Error("Auth collection not found");
5745
+ }
5746
+ const userFields = mapUserFields(collection, ["_rawData"]);
5747
+ if (!userFields.length) {
5748
+ throw new Error(`No user field found in collection ${collection.name}`);
5749
+ }
5750
+ if (userFields.length > 1) {
5751
+ throw new Error(
5752
+ `Multiple user fields found in collection ${collection.name}`
5753
+ );
5754
+ }
5755
+ const userField = userFields[0];
5756
+ const realPath = `${collection.path}/index.json`;
5757
+ const userDoc = await resolver.getDocument(realPath);
5758
+ const users = get(userDoc, userField.path);
5759
+ if (!users) {
5760
+ throw new Error("No users found");
5761
+ }
5762
+ return { collection, userField, users, userDoc, realPath };
5763
+ }
5764
+ function findUserInCollection(users, userField, userSub) {
5765
+ const { idFieldName } = userField;
5766
+ if (!idFieldName) {
5767
+ throw new Error("No uid field found on user field");
5768
+ }
5769
+ return users.find((u) => u[idFieldName] === userSub) || null;
5770
+ }
5771
+ async function handleAuthenticate({
5772
+ tinaSchema,
5773
+ resolver,
5774
+ sub,
5775
+ password,
5776
+ ctxUser
5777
+ }) {
5778
+ const userSub = sub || ctxUser?.sub;
5779
+ const { userField, users } = await getUserDocumentContext(
5780
+ tinaSchema,
5781
+ resolver
5782
+ );
5783
+ const user = findUserInCollection(users, userField, userSub);
5784
+ if (!user) {
5785
+ return null;
5786
+ }
5787
+ const { passwordFieldName } = userField;
5788
+ const saltedHash = get(user, [passwordFieldName || "", "value"]);
5789
+ if (!saltedHash) {
5790
+ throw new Error("No password field found on user field");
5791
+ }
5792
+ const matches = await checkPasswordHash({
5793
+ saltedHash,
5794
+ password
5795
+ });
5796
+ return matches ? user : null;
5797
+ }
5798
+ async function handleAuthorize({
5799
+ tinaSchema,
5800
+ resolver,
5801
+ sub,
5802
+ ctxUser
5803
+ }) {
5804
+ const userSub = sub || ctxUser?.sub;
5805
+ const { userField, users } = await getUserDocumentContext(
5806
+ tinaSchema,
5807
+ resolver
5808
+ );
5809
+ const user = findUserInCollection(users, userField, userSub);
5810
+ return user ? user : null;
5811
+ }
5812
+ async function handleUpdatePassword({
5813
+ tinaSchema,
5814
+ resolver,
5815
+ password,
5816
+ ctxUser
5817
+ }) {
5818
+ if (!ctxUser?.sub) {
5819
+ throw new Error("Not authorized");
5820
+ }
5821
+ if (!password) {
5822
+ throw new Error("No password provided");
5823
+ }
5824
+ const { collection, userField, users, realPath } = await getUserDocumentContext(tinaSchema, resolver);
5825
+ const { idFieldName, passwordFieldName } = userField;
5826
+ const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5827
+ if (!user) {
5828
+ throw new Error("Not authorized");
5829
+ }
5830
+ user[passwordFieldName] = {
5831
+ value: password,
5832
+ passwordChangeRequired: false
5833
+ };
5834
+ const params = {};
5835
+ (0, import_compat.set)(
5836
+ params,
5837
+ userField.path.slice(1),
5838
+ // remove _rawData from users path
5839
+ users.map((u) => {
5840
+ if (user[idFieldName] === u[idFieldName]) {
5841
+ return user;
5842
+ }
5843
+ return {
5844
+ // don't overwrite other users' passwords
5845
+ ...u,
5846
+ [passwordFieldName]: {
5847
+ ...u[passwordFieldName],
5848
+ value: ""
5849
+ }
5850
+ };
5851
+ })
5852
+ );
5853
+ await resolver.updateResolveDocument({
5854
+ collection,
5855
+ args: { params },
5856
+ realPath,
5857
+ isCollectionSpecific: true,
5858
+ isAddPendingDocument: false
5859
+ });
5860
+ return true;
5861
+ }
5263
5862
 
5264
5863
  // src/error.ts
5265
5864
  var import_graphql4 = require("graphql");
@@ -5281,9 +5880,8 @@ var resolve = async ({
5281
5880
  isAudit,
5282
5881
  ctxUser
5283
5882
  }) => {
5284
- var _a;
5285
5883
  try {
5286
- const verboseValue = verbose != null ? verbose : true;
5884
+ const verboseValue = verbose ?? true;
5287
5885
  const graphQLSchemaAst = await database.getGraphQLSchema();
5288
5886
  if (!graphQLSchemaAst) {
5289
5887
  throw new import_graphql5.GraphQLError("GraphQL schema not found");
@@ -5291,8 +5889,11 @@ var resolve = async ({
5291
5889
  const graphQLSchema = (0, import_graphql5.buildASTSchema)(graphQLSchemaAst);
5292
5890
  const tinaConfig = await database.getTinaSchema();
5293
5891
  const tinaSchema = await createSchema({
5892
+ // TODO: please update all the types to import from @tinacms/schema-tools
5893
+ // @ts-ignore
5294
5894
  schema: tinaConfig,
5295
- flags: (_a = tinaConfig == null ? void 0 : tinaConfig.meta) == null ? void 0 : _a.flags
5895
+ // @ts-ignore
5896
+ flags: tinaConfig?.meta?.flags
5296
5897
  });
5297
5898
  const resolver = createResolver({
5298
5899
  config,
@@ -5308,8 +5909,7 @@ var resolve = async ({
5308
5909
  database
5309
5910
  },
5310
5911
  typeResolver: async (source, _args, info) => {
5311
- if (source.__typename)
5312
- return source.__typename;
5912
+ if (source.__typename) return source.__typename;
5313
5913
  const namedType = (0, import_graphql5.getNamedType)(info.returnType).toString();
5314
5914
  const lookup = await database.getLookup(namedType);
5315
5915
  if (lookup.resolveType === "unionData") {
@@ -5318,7 +5918,6 @@ var resolve = async ({
5318
5918
  throw new Error(`Unable to find lookup key for ${namedType}`);
5319
5919
  },
5320
5920
  fieldResolver: async (source = {}, _args = {}, _context, info) => {
5321
- var _a2, _b, _c, _d;
5322
5921
  try {
5323
5922
  const args = JSON.parse(JSON.stringify(_args));
5324
5923
  const returnType = (0, import_graphql5.getNamedType)(info.returnType).toString();
@@ -5335,8 +5934,7 @@ var resolve = async ({
5335
5934
  );
5336
5935
  const hasDocuments2 = collectionNode2.selectionSet.selections.find(
5337
5936
  (x) => {
5338
- var _a3;
5339
- return ((_a3 = x == null ? void 0 : x.name) == null ? void 0 : _a3.value) === "documents";
5937
+ return x?.name?.value === "documents";
5340
5938
  }
5341
5939
  );
5342
5940
  return tinaSchema.getCollections().map((collection) => {
@@ -5352,8 +5950,7 @@ var resolve = async ({
5352
5950
  );
5353
5951
  const hasDocuments = collectionNode.selectionSet.selections.find(
5354
5952
  (x) => {
5355
- var _a3;
5356
- return ((_a3 = x == null ? void 0 : x.name) == null ? void 0 : _a3.value) === "documents";
5953
+ return x?.name?.value === "documents";
5357
5954
  }
5358
5955
  );
5359
5956
  return resolver.resolveCollection(
@@ -5371,123 +5968,42 @@ var resolve = async ({
5371
5968
  );
5372
5969
  }
5373
5970
  }
5374
- if (info.fieldName === "authenticate" || info.fieldName === "authorize") {
5375
- const sub = args.sub || (ctxUser == null ? void 0 : ctxUser.sub);
5376
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5377
- if (!collection) {
5378
- throw new Error("Auth collection not found");
5379
- }
5380
- const userFields = mapUserFields(collection, ["_rawData"]);
5381
- if (!userFields.length) {
5382
- throw new Error(
5383
- `No user field found in collection ${collection.name}`
5384
- );
5385
- }
5386
- if (userFields.length > 1) {
5387
- throw new Error(
5388
- `Multiple user fields found in collection ${collection.name}`
5389
- );
5390
- }
5391
- const userField = userFields[0];
5392
- const realPath = `${collection.path}/index.json`;
5393
- const userDoc = await resolver.getDocument(realPath);
5394
- const users = get(userDoc, userField.path);
5395
- if (!users) {
5396
- throw new Error("No users found");
5397
- }
5398
- const { idFieldName, passwordFieldName } = userField;
5399
- if (!idFieldName) {
5400
- throw new Error("No uid field found on user field");
5401
- }
5402
- const user = users.find((u) => u[idFieldName] === sub);
5403
- if (!user) {
5404
- return null;
5405
- }
5406
- if (info.fieldName === "authenticate") {
5407
- const saltedHash = get(user, [passwordFieldName || "", "value"]);
5408
- if (!saltedHash) {
5409
- throw new Error("No password field found on user field");
5410
- }
5411
- const matches = await checkPasswordHash({
5412
- saltedHash,
5413
- password: args.password
5414
- });
5415
- if (matches) {
5416
- return user;
5417
- }
5418
- return null;
5419
- }
5420
- return user;
5971
+ if (info.fieldName === "authenticate") {
5972
+ return handleAuthenticate({
5973
+ tinaSchema,
5974
+ resolver,
5975
+ sub: args.sub,
5976
+ password: args.password,
5977
+ info,
5978
+ ctxUser
5979
+ });
5980
+ }
5981
+ if (info.fieldName === "authorize") {
5982
+ return handleAuthorize({
5983
+ tinaSchema,
5984
+ resolver,
5985
+ sub: args.sub,
5986
+ info,
5987
+ ctxUser
5988
+ });
5421
5989
  }
5422
5990
  if (info.fieldName === "updatePassword") {
5423
- if (!(ctxUser == null ? void 0 : ctxUser.sub)) {
5424
- throw new Error("Not authorized");
5425
- }
5426
- if (!args.password) {
5427
- throw new Error("No password provided");
5428
- }
5429
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5430
- if (!collection) {
5431
- throw new Error("Auth collection not found");
5432
- }
5433
- const userFields = mapUserFields(collection, ["_rawData"]);
5434
- if (!userFields.length) {
5435
- throw new Error(
5436
- `No user field found in collection ${collection.name}`
5437
- );
5438
- }
5439
- if (userFields.length > 1) {
5440
- throw new Error(
5441
- `Multiple user fields found in collection ${collection.name}`
5442
- );
5443
- }
5444
- const userField = userFields[0];
5445
- const realPath = `${collection.path}/index.json`;
5446
- const userDoc = await resolver.getDocument(realPath);
5447
- const users = get(userDoc, userField.path);
5448
- if (!users) {
5449
- throw new Error("No users found");
5450
- }
5451
- const { idFieldName, passwordFieldName } = userField;
5452
- const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5453
- if (!user) {
5454
- throw new Error("Not authorized");
5455
- }
5456
- user[passwordFieldName] = {
5457
- value: args.password,
5458
- passwordChangeRequired: false
5459
- };
5460
- const params = {};
5461
- (0, import_lodash4.default)(
5462
- params,
5463
- userField.path.slice(1),
5464
- users.map((u) => {
5465
- if (user[idFieldName] === u[idFieldName]) {
5466
- return user;
5467
- }
5468
- return {
5469
- ...u,
5470
- [passwordFieldName]: {
5471
- ...u[passwordFieldName],
5472
- value: ""
5473
- }
5474
- };
5475
- })
5476
- );
5477
- await resolver.updateResolveDocument({
5478
- collection,
5479
- args: { params },
5480
- realPath,
5481
- isCollectionSpecific: true,
5482
- isAddPendingDocument: false
5991
+ return handleUpdatePassword({
5992
+ tinaSchema,
5993
+ resolver,
5994
+ password: args.password,
5995
+ info,
5996
+ ctxUser
5483
5997
  });
5484
- return true;
5485
5998
  }
5486
5999
  if (!lookup) {
5487
6000
  return value;
5488
6001
  }
5489
6002
  const isCreation = lookup[info.fieldName] === "create";
5490
6003
  switch (lookup.resolveType) {
6004
+ /**
6005
+ * `node(id: $id)`
6006
+ */
5491
6007
  case "nodeDocument":
5492
6008
  assertShape(
5493
6009
  args,
@@ -5498,7 +6014,7 @@ var resolve = async ({
5498
6014
  if (typeof value === "string" && value !== "") {
5499
6015
  return resolver.getDocument(value);
5500
6016
  }
5501
- if ((args == null ? void 0 : args.collection) && info.fieldName === "addPendingDocument") {
6017
+ if (args?.collection && info.fieldName === "addPendingDocument") {
5502
6018
  return resolver.resolveDocument({
5503
6019
  args: { ...args, params: {} },
5504
6020
  collection: args.collection,
@@ -5519,15 +6035,19 @@ var resolve = async ({
5519
6035
  collection: args.collection,
5520
6036
  isMutation,
5521
6037
  isCreation,
6038
+ // Right now this is the only case for deletion
5522
6039
  isDeletion: info.fieldName === "deleteDocument",
5523
6040
  isFolderCreation: info.fieldName === "createFolder",
5524
- isUpdateName: Boolean((_a2 = args == null ? void 0 : args.params) == null ? void 0 : _a2.relativePath),
6041
+ isUpdateName: Boolean(args?.params?.relativePath),
5525
6042
  isAddPendingDocument: false,
5526
6043
  isCollectionSpecific: false
5527
6044
  });
5528
6045
  return result;
5529
6046
  }
5530
6047
  return value;
6048
+ /**
6049
+ * eg `getMovieDocument.data.actors`
6050
+ */
5531
6051
  case "multiCollectionDocumentList":
5532
6052
  if (Array.isArray(value)) {
5533
6053
  return {
@@ -5537,9 +6057,17 @@ var resolve = async ({
5537
6057
  })
5538
6058
  };
5539
6059
  }
5540
- if (info.fieldName === "documents" && (value == null ? void 0 : value.collection) && (value == null ? void 0 : value.hasDocuments)) {
6060
+ if (info.fieldName === "documents" && value?.collection && value?.hasDocuments) {
5541
6061
  let filter = args.filter;
5542
- if (typeof (args == null ? void 0 : args.filter) !== "undefined" && (args == null ? void 0 : args.filter) !== null && typeof ((_b = value == null ? void 0 : value.collection) == null ? void 0 : _b.name) === "string" && Object.keys(args.filter).includes((_c = value == null ? void 0 : value.collection) == null ? void 0 : _c.name) && typeof args.filter[(_d = value == null ? void 0 : value.collection) == null ? void 0 : _d.name] !== "undefined") {
6062
+ if (
6063
+ // 1. Make sure that the filter exists
6064
+ typeof args?.filter !== "undefined" && args?.filter !== null && // 2. Make sure that the collection name exists
6065
+ // @ts-ignore
6066
+ typeof value?.collection?.name === "string" && // 3. Make sure that the collection name is in the filter and is not undefined
6067
+ // @ts-ignore
6068
+ Object.keys(args.filter).includes(value?.collection?.name) && // @ts-ignore
6069
+ typeof args.filter[value?.collection?.name] !== "undefined"
6070
+ ) {
5543
6071
  filter = args.filter[value.collection.name];
5544
6072
  }
5545
6073
  return resolver.resolveCollectionConnection({
@@ -5547,12 +6075,20 @@ var resolve = async ({
5547
6075
  ...args,
5548
6076
  filter
5549
6077
  },
6078
+ // @ts-ignore
5550
6079
  collection: value.collection
5551
6080
  });
5552
6081
  }
5553
6082
  throw new Error(
5554
6083
  `Expected an array for result of ${info.fieldName} at ${info.path}`
5555
6084
  );
6085
+ /**
6086
+ * Collections-specific getter
6087
+ * eg. `getPostDocument`/`createPostDocument`/`updatePostDocument`
6088
+ *
6089
+ * if coming from a query result
6090
+ * the field will be `node`
6091
+ */
5556
6092
  case "collectionDocument": {
5557
6093
  if (value) {
5558
6094
  return value;
@@ -5567,11 +6103,32 @@ var resolve = async ({
5567
6103
  });
5568
6104
  return result;
5569
6105
  }
6106
+ /**
6107
+ * Collections-specific list getter
6108
+ * eg. `getPageList`
6109
+ */
5570
6110
  case "collectionDocumentList":
5571
6111
  return resolver.resolveCollectionConnection({
5572
6112
  args,
5573
6113
  collection: tinaSchema.getCollection(lookup.collection)
5574
6114
  });
6115
+ /**
6116
+ * A polymorphic data set, it can be from a document's data
6117
+ * of any nested object which can be one of many shapes
6118
+ *
6119
+ * ```graphql
6120
+ * getPostDocument(relativePath: $relativePath) {
6121
+ * data {...} <- this part
6122
+ * }
6123
+ * ```
6124
+ * ```graphql
6125
+ * getBlockDocument(relativePath: $relativePath) {
6126
+ * data {
6127
+ * blocks {...} <- or this part
6128
+ * }
6129
+ * }
6130
+ * ```
6131
+ */
5575
6132
  case "unionData":
5576
6133
  if (!value) {
5577
6134
  if (args.relativePath) {
@@ -5636,8 +6193,7 @@ var TinaLevelClient = class extends import_many_level.ManyLevelGuest {
5636
6193
  this.port = port || 9e3;
5637
6194
  }
5638
6195
  openConnection() {
5639
- if (this._connected)
5640
- return;
6196
+ if (this._connected) return;
5641
6197
  const socket = (0, import_net.connect)(this.port);
5642
6198
  (0, import_readable_stream.pipeline)(socket, this.createRpcStream(), socket, () => {
5643
6199
  this._connected = false;
@@ -5647,15 +6203,15 @@ var TinaLevelClient = class extends import_many_level.ManyLevelGuest {
5647
6203
  };
5648
6204
 
5649
6205
  // src/database/index.ts
5650
- var import_node_path = __toESM(require("path"));
6206
+ var import_node_path = __toESM(require("node:path"));
5651
6207
  var import_graphql6 = require("graphql");
5652
6208
  var import_micromatch2 = __toESM(require("micromatch"));
5653
6209
  var import_js_sha12 = __toESM(require("js-sha1"));
5654
- var import_lodash5 = __toESM(require("lodash.set"));
6210
+ var import_compat2 = require("es-toolkit/compat");
5655
6211
  var createLocalDatabase = (config) => {
5656
- const level = new TinaLevelClient(config == null ? void 0 : config.port);
6212
+ const level = new TinaLevelClient(config?.port);
5657
6213
  level.openConnection();
5658
- const fsBridge = new FilesystemBridge((config == null ? void 0 : config.rootPath) || process.cwd());
6214
+ const fsBridge = new FilesystemBridge(config?.rootPath || process.cwd());
5659
6215
  return new Database({
5660
6216
  bridge: fsBridge,
5661
6217
  ...config || {},
@@ -5728,7 +6284,7 @@ var Database = class {
5728
6284
  );
5729
6285
  }
5730
6286
  const metadata = await metadataLevel.get(`metadata_${key}`);
5731
- return metadata == null ? void 0 : metadata.value;
6287
+ return metadata?.value;
5732
6288
  };
5733
6289
  this.setMetadata = async (key, value) => {
5734
6290
  await this.initLevel();
@@ -5750,14 +6306,14 @@ var Database = class {
5750
6306
  let level = this.contentLevel;
5751
6307
  if (this.appLevel) {
5752
6308
  collection = await this.collectionForPath(filepath);
5753
- if (collection == null ? void 0 : collection.isDetached) {
6309
+ if (collection?.isDetached) {
5754
6310
  level = this.appLevel.sublevel(collection.name, SUBLEVEL_OPTIONS);
5755
6311
  }
5756
6312
  }
5757
6313
  const contentObject = await level.sublevel(
5758
6314
  CONTENT_ROOT_PREFIX,
5759
6315
  SUBLEVEL_OPTIONS
5760
- ).get((0, import_schema_tools3.normalizePath)(filepath));
6316
+ ).get((0, import_schema_tools4.normalizePath)(filepath));
5761
6317
  if (!contentObject) {
5762
6318
  throw new NotFoundError(`Unable to find record ${filepath}`);
5763
6319
  }
@@ -5781,9 +6337,10 @@ var Database = class {
5781
6337
  collection
5782
6338
  );
5783
6339
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
5784
- const collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
5785
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
5786
- if (!(collection == null ? void 0 : collection.isDetached)) {
6340
+ const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6341
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6342
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
6343
+ if (!collection?.isDetached) {
5787
6344
  if (this.bridge) {
5788
6345
  await this.bridge.put(normalizedPath, stringifiedFile);
5789
6346
  }
@@ -5801,7 +6358,7 @@ var Database = class {
5801
6358
  }
5802
6359
  }
5803
6360
  let level = this.contentLevel;
5804
- if (collection == null ? void 0 : collection.isDetached) {
6361
+ if (collection?.isDetached) {
5805
6362
  level = this.appLevel.sublevel(collection.name, SUBLEVEL_OPTIONS);
5806
6363
  }
5807
6364
  const folderTreeBuilder = new FolderTreeBuilder();
@@ -5810,17 +6367,26 @@ var Database = class {
5810
6367
  let delOps = [];
5811
6368
  if (!isGitKeep(normalizedPath, collection)) {
5812
6369
  putOps = [
6370
+ ...makeRefOpsForDocument(
6371
+ normalizedPath,
6372
+ collection?.name,
6373
+ collectionReferences,
6374
+ dataFields,
6375
+ "put",
6376
+ level
6377
+ ),
5813
6378
  ...makeIndexOpsForDocument(
5814
6379
  normalizedPath,
5815
- collection == null ? void 0 : collection.name,
6380
+ collection?.name,
5816
6381
  collectionIndexDefinitions,
5817
6382
  dataFields,
5818
6383
  "put",
5819
6384
  level
5820
6385
  ),
6386
+ // folder indices
5821
6387
  ...makeIndexOpsForDocument(
5822
6388
  normalizedPath,
5823
- `${collection == null ? void 0 : collection.name}_${folderKey}`,
6389
+ `${collection?.name}_${folderKey}`,
5824
6390
  collectionIndexDefinitions,
5825
6391
  dataFields,
5826
6392
  "put",
@@ -5832,17 +6398,26 @@ var Database = class {
5832
6398
  SUBLEVEL_OPTIONS
5833
6399
  ).get(normalizedPath);
5834
6400
  delOps = existingItem ? [
6401
+ ...makeRefOpsForDocument(
6402
+ normalizedPath,
6403
+ collection?.name,
6404
+ collectionReferences,
6405
+ existingItem,
6406
+ "del",
6407
+ level
6408
+ ),
5835
6409
  ...makeIndexOpsForDocument(
5836
6410
  normalizedPath,
5837
- collection == null ? void 0 : collection.name,
6411
+ collection?.name,
5838
6412
  collectionIndexDefinitions,
5839
6413
  existingItem,
5840
6414
  "del",
5841
6415
  level
5842
6416
  ),
6417
+ // folder indices
5843
6418
  ...makeIndexOpsForDocument(
5844
6419
  normalizedPath,
5845
- `${collection == null ? void 0 : collection.name}_${folderKey}`,
6420
+ `${collection?.name}_${folderKey}`,
5846
6421
  collectionIndexDefinitions,
5847
6422
  existingItem,
5848
6423
  "del",
@@ -5866,7 +6441,6 @@ var Database = class {
5866
6441
  await level.batch(ops);
5867
6442
  };
5868
6443
  this.put = async (filepath, data, collectionName) => {
5869
- var _a, _b;
5870
6444
  await this.initLevel();
5871
6445
  try {
5872
6446
  if (SYSTEM_FILES.includes(filepath)) {
@@ -5877,15 +6451,16 @@ var Database = class {
5877
6451
  const indexDefinitions = await this.getIndexDefinitions(
5878
6452
  this.contentLevel
5879
6453
  );
5880
- collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collectionName];
6454
+ collectionIndexDefinitions = indexDefinitions?.[collectionName];
5881
6455
  }
5882
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
6456
+ const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
6457
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
5883
6458
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5884
6459
  const collection = await this.collectionForPath(filepath);
5885
6460
  if (!collection) {
5886
6461
  throw new import_graphql6.GraphQLError(`Unable to find collection for ${filepath}.`);
5887
6462
  }
5888
- if (((_a = collection.match) == null ? void 0 : _a.exclude) || ((_b = collection.match) == null ? void 0 : _b.include)) {
6463
+ if (collection.match?.exclude || collection.match?.include) {
5889
6464
  const matches = this.tinaSchema.getMatches({ collection });
5890
6465
  const match = import_micromatch2.default.isMatch(filepath, matches);
5891
6466
  if (!match) {
@@ -5899,7 +6474,7 @@ var Database = class {
5899
6474
  const stringifiedFile = filepath.endsWith(
5900
6475
  `.gitkeep.${collection.format || "md"}`
5901
6476
  ) ? "" : await this.stringifyFile(filepath, dataFields, collection);
5902
- if (!(collection == null ? void 0 : collection.isDetached)) {
6477
+ if (!collection?.isDetached) {
5903
6478
  if (this.bridge) {
5904
6479
  await this.bridge.put(normalizedPath, stringifiedFile);
5905
6480
  }
@@ -5921,11 +6496,19 @@ var Database = class {
5921
6496
  filepath,
5922
6497
  collection.path || ""
5923
6498
  );
5924
- const level = (collection == null ? void 0 : collection.isDetached) ? this.appLevel.sublevel(collection == null ? void 0 : collection.name, SUBLEVEL_OPTIONS) : this.contentLevel;
6499
+ const level = collection?.isDetached ? this.appLevel.sublevel(collection?.name, SUBLEVEL_OPTIONS) : this.contentLevel;
5925
6500
  let putOps = [];
5926
6501
  let delOps = [];
5927
6502
  if (!isGitKeep(normalizedPath, collection)) {
5928
6503
  putOps = [
6504
+ ...makeRefOpsForDocument(
6505
+ normalizedPath,
6506
+ collectionName,
6507
+ collectionReferences,
6508
+ dataFields,
6509
+ "put",
6510
+ level
6511
+ ),
5929
6512
  ...makeIndexOpsForDocument(
5930
6513
  normalizedPath,
5931
6514
  collectionName,
@@ -5934,9 +6517,10 @@ var Database = class {
5934
6517
  "put",
5935
6518
  level
5936
6519
  ),
6520
+ // folder indices
5937
6521
  ...makeIndexOpsForDocument(
5938
6522
  normalizedPath,
5939
- `${collection == null ? void 0 : collection.name}_${folderKey}`,
6523
+ `${collection?.name}_${folderKey}`,
5940
6524
  collectionIndexDefinitions,
5941
6525
  dataFields,
5942
6526
  "put",
@@ -5948,6 +6532,14 @@ var Database = class {
5948
6532
  SUBLEVEL_OPTIONS
5949
6533
  ).get(normalizedPath);
5950
6534
  delOps = existingItem ? [
6535
+ ...makeRefOpsForDocument(
6536
+ normalizedPath,
6537
+ collectionName,
6538
+ collectionReferences,
6539
+ existingItem,
6540
+ "del",
6541
+ level
6542
+ ),
5951
6543
  ...makeIndexOpsForDocument(
5952
6544
  normalizedPath,
5953
6545
  collectionName,
@@ -5956,9 +6548,10 @@ var Database = class {
5956
6548
  "del",
5957
6549
  level
5958
6550
  ),
6551
+ // folder indices
5959
6552
  ...makeIndexOpsForDocument(
5960
6553
  normalizedPath,
5961
- `${collection == null ? void 0 : collection.name}_${folderKey}`,
6554
+ `${collection?.name}_${folderKey}`,
5962
6555
  collectionIndexDefinitions,
5963
6556
  existingItem,
5964
6557
  "del",
@@ -5989,8 +6582,7 @@ var Database = class {
5989
6582
  throw new TinaFetchError(`Error in PUT for ${filepath}`, {
5990
6583
  originalError: error,
5991
6584
  file: filepath,
5992
- collection: collectionName,
5993
- stack: error.stack
6585
+ collection: collectionName
5994
6586
  });
5995
6587
  }
5996
6588
  };
@@ -6033,9 +6625,10 @@ var Database = class {
6033
6625
  aliasedData,
6034
6626
  extension,
6035
6627
  writeTemplateKey,
6628
+ //templateInfo.type === 'union',
6036
6629
  {
6037
- frontmatterFormat: collection == null ? void 0 : collection.frontmatterFormat,
6038
- frontmatterDelimiters: collection == null ? void 0 : collection.frontmatterDelimiters
6630
+ frontmatterFormat: collection?.frontmatterFormat,
6631
+ frontmatterDelimiters: collection?.frontmatterDelimiters
6039
6632
  }
6040
6633
  );
6041
6634
  };
@@ -6050,7 +6643,7 @@ var Database = class {
6050
6643
  };
6051
6644
  this.getLookup = async (returnType) => {
6052
6645
  await this.initLevel();
6053
- const lookupPath = (0, import_schema_tools3.normalizePath)(
6646
+ const lookupPath = (0, import_schema_tools4.normalizePath)(
6054
6647
  import_node_path.default.join(this.getGeneratedFolder(), `_lookup.json`)
6055
6648
  );
6056
6649
  if (!this._lookup) {
@@ -6063,7 +6656,7 @@ var Database = class {
6063
6656
  };
6064
6657
  this.getGraphQLSchema = async () => {
6065
6658
  await this.initLevel();
6066
- const graphqlPath = (0, import_schema_tools3.normalizePath)(
6659
+ const graphqlPath = (0, import_schema_tools4.normalizePath)(
6067
6660
  import_node_path.default.join(this.getGeneratedFolder(), `_graphql.json`)
6068
6661
  );
6069
6662
  return await this.contentLevel.sublevel(
@@ -6071,11 +6664,12 @@ var Database = class {
6071
6664
  SUBLEVEL_OPTIONS
6072
6665
  ).get(graphqlPath);
6073
6666
  };
6667
+ //TODO - is there a reason why the database fetches some config with "bridge.get", and some with "store.get"?
6074
6668
  this.getGraphQLSchemaFromBridge = async () => {
6075
6669
  if (!this.bridge) {
6076
6670
  throw new Error(`No bridge configured`);
6077
6671
  }
6078
- const graphqlPath = (0, import_schema_tools3.normalizePath)(
6672
+ const graphqlPath = (0, import_schema_tools4.normalizePath)(
6079
6673
  import_node_path.default.join(this.getGeneratedFolder(), `_graphql.json`)
6080
6674
  );
6081
6675
  const _graphql = await this.bridge.get(graphqlPath);
@@ -6083,7 +6677,7 @@ var Database = class {
6083
6677
  };
6084
6678
  this.getTinaSchema = async (level) => {
6085
6679
  await this.initLevel();
6086
- const schemaPath = (0, import_schema_tools3.normalizePath)(
6680
+ const schemaPath = (0, import_schema_tools4.normalizePath)(
6087
6681
  import_node_path.default.join(this.getGeneratedFolder(), `_schema.json`)
6088
6682
  );
6089
6683
  return await (level || this.contentLevel).sublevel(
@@ -6099,7 +6693,7 @@ var Database = class {
6099
6693
  const schema = existingSchema || await this.getTinaSchema(level || this.contentLevel);
6100
6694
  if (!schema) {
6101
6695
  throw new Error(
6102
- `Unable to get schema from level db: ${(0, import_schema_tools3.normalizePath)(
6696
+ `Unable to get schema from level db: ${(0, import_schema_tools4.normalizePath)(
6103
6697
  import_node_path.default.join(this.getGeneratedFolder(), `_schema.json`)
6104
6698
  )}`
6105
6699
  );
@@ -6107,6 +6701,22 @@ var Database = class {
6107
6701
  this.tinaSchema = await createSchema({ schema });
6108
6702
  return this.tinaSchema;
6109
6703
  };
6704
+ this.getCollectionReferences = async (level) => {
6705
+ if (this.collectionReferences) {
6706
+ return this.collectionReferences;
6707
+ }
6708
+ const result = {};
6709
+ const schema = await this.getSchema(level || this.contentLevel);
6710
+ const collections = schema.getCollections();
6711
+ for (const collection of collections) {
6712
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6713
+ collection.name
6714
+ );
6715
+ result[collection.name] = collectionReferences;
6716
+ }
6717
+ this.collectionReferences = result;
6718
+ return result;
6719
+ };
6110
6720
  this.getIndexDefinitions = async (level) => {
6111
6721
  if (!this.collectionIndexDefinitions) {
6112
6722
  await new Promise(async (resolve2, reject) => {
@@ -6116,10 +6726,53 @@ var Database = class {
6116
6726
  const collections = schema.getCollections();
6117
6727
  for (const collection of collections) {
6118
6728
  const indexDefinitions = {
6119
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6729
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6730
+ // provide a default sort key which is the file sort
6731
+ // pseudo-index for the collection's references
6732
+ [REFS_COLLECTIONS_SORT_KEY]: {
6733
+ fields: [
6734
+ {
6735
+ name: REFS_REFERENCE_FIELD,
6736
+ type: "string",
6737
+ list: false
6738
+ },
6739
+ {
6740
+ name: REFS_PATH_FIELD,
6741
+ type: "string",
6742
+ list: false
6743
+ }
6744
+ ]
6745
+ }
6120
6746
  };
6121
- if (collection.fields) {
6122
- for (const field of collection.fields) {
6747
+ let fields = [];
6748
+ if (collection.templates) {
6749
+ const templateFieldMap = {};
6750
+ const conflictedFields = /* @__PURE__ */ new Set();
6751
+ for (const template of collection.templates) {
6752
+ for (const field of template.fields) {
6753
+ if (!templateFieldMap[field.name]) {
6754
+ templateFieldMap[field.name] = field;
6755
+ } else {
6756
+ if (templateFieldMap[field.name].type !== field.type) {
6757
+ console.warn(
6758
+ `Field ${field.name} has conflicting types in templates - skipping index`
6759
+ );
6760
+ conflictedFields.add(field.name);
6761
+ }
6762
+ }
6763
+ }
6764
+ }
6765
+ for (const conflictedField in conflictedFields) {
6766
+ delete templateFieldMap[conflictedField];
6767
+ }
6768
+ for (const field of Object.values(templateFieldMap)) {
6769
+ fields.push(field);
6770
+ }
6771
+ } else if (collection.fields) {
6772
+ fields = collection.fields;
6773
+ }
6774
+ if (fields) {
6775
+ for (const field of fields) {
6123
6776
  if (field.indexed !== void 0 && field.indexed === false || field.type === "object") {
6124
6777
  continue;
6125
6778
  }
@@ -6144,8 +6797,8 @@ var Database = class {
6144
6797
  );
6145
6798
  return {
6146
6799
  name: indexField.name,
6147
- type: field == null ? void 0 : field.type,
6148
- list: !!(field == null ? void 0 : field.list)
6800
+ type: field?.type,
6801
+ list: !!field?.list
6149
6802
  };
6150
6803
  })
6151
6804
  };
@@ -6171,7 +6824,6 @@ var Database = class {
6171
6824
  return true;
6172
6825
  };
6173
6826
  this.query = async (queryOptions, hydrator) => {
6174
- var _a;
6175
6827
  await this.initLevel();
6176
6828
  const {
6177
6829
  first,
@@ -6199,14 +6851,14 @@ var Database = class {
6199
6851
  const allIndexDefinitions = await this.getIndexDefinitions(
6200
6852
  this.contentLevel
6201
6853
  );
6202
- const indexDefinitions = allIndexDefinitions == null ? void 0 : allIndexDefinitions[collection.name];
6854
+ const indexDefinitions = allIndexDefinitions?.[collection.name];
6203
6855
  if (!indexDefinitions) {
6204
6856
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6205
6857
  }
6206
6858
  const filterChain = coerceFilterChainOperands(rawFilterChain);
6207
- const indexDefinition = sort && (indexDefinitions == null ? void 0 : indexDefinitions[sort]);
6859
+ const indexDefinition = sort && indexDefinitions?.[sort];
6208
6860
  const filterSuffixes = indexDefinition && makeFilterSuffixes(filterChain, indexDefinition);
6209
- const level = (collection == null ? void 0 : collection.isDetached) ? this.appLevel.sublevel(collection == null ? void 0 : collection.name, SUBLEVEL_OPTIONS) : this.contentLevel;
6861
+ const level = collection?.isDetached ? this.appLevel.sublevel(collection?.name, SUBLEVEL_OPTIONS) : this.contentLevel;
6210
6862
  const rootLevel = level.sublevel(
6211
6863
  CONTENT_ROOT_PREFIX,
6212
6864
  SUBLEVEL_OPTIONS
@@ -6216,17 +6868,17 @@ var Database = class {
6216
6868
  SUBLEVEL_OPTIONS
6217
6869
  ).sublevel(sort, SUBLEVEL_OPTIONS) : rootLevel;
6218
6870
  if (!query.gt && !query.gte) {
6219
- query.gte = (filterSuffixes == null ? void 0 : filterSuffixes.left) ? filterSuffixes.left : "";
6871
+ query.gte = filterSuffixes?.left ? filterSuffixes.left : "";
6220
6872
  }
6221
6873
  if (!query.lt && !query.lte) {
6222
- query.lte = (filterSuffixes == null ? void 0 : filterSuffixes.right) ? `${filterSuffixes.right}\uFFFF` : "\uFFFF";
6874
+ query.lte = filterSuffixes?.right ? `${filterSuffixes.right}\uFFFF` : "\uFFFF";
6223
6875
  }
6224
6876
  let edges = [];
6225
6877
  let startKey = "";
6226
6878
  let endKey = "";
6227
6879
  let hasPreviousPage = false;
6228
6880
  let hasNextPage = false;
6229
- const fieldsPattern = ((_a = indexDefinition == null ? void 0 : indexDefinition.fields) == null ? void 0 : _a.length) ? `${indexDefinition.fields.map((p) => `(?<${p.name}>.+)${INDEX_KEY_FIELD_SEPARATOR}`).join("")}` : "";
6881
+ const fieldsPattern = indexDefinition?.fields?.length ? `${indexDefinition.fields.map((p) => `(?<${p.name}>.+)${INDEX_KEY_FIELD_SEPARATOR}`).join("")}` : "";
6230
6882
  const valuesRegex = indexDefinition ? new RegExp(`^${fieldsPattern}(?<_filepath_>.+)`) : new RegExp(`^(?<_filepath_>.+)`);
6231
6883
  const itemFilter = makeFilter({ filterChain });
6232
6884
  const iterator = sublevel.iterator(query);
@@ -6268,29 +6920,35 @@ var Database = class {
6268
6920
  }
6269
6921
  startKey = startKey || key || "";
6270
6922
  endKey = key || "";
6271
- edges = [...edges, { cursor: key, path: filepath }];
6923
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6272
6924
  }
6273
6925
  return {
6274
- edges: await sequential(edges, async (edge) => {
6275
- try {
6276
- const node = await hydrator(edge.path);
6277
- return {
6278
- node,
6279
- cursor: btoa(edge.cursor)
6280
- };
6281
- } catch (error) {
6282
- console.log(error);
6283
- if (error instanceof Error && (!edge.path.includes(".tina/__generated__/_graphql.json") || !edge.path.includes("tina/__generated__/_graphql.json"))) {
6284
- throw new TinaQueryError({
6285
- originalError: error,
6286
- file: edge.path,
6287
- collection: collection.name,
6288
- stack: error.stack
6289
- });
6926
+ edges: await sequential(
6927
+ edges,
6928
+ async ({
6929
+ cursor,
6930
+ path: path7,
6931
+ value
6932
+ }) => {
6933
+ try {
6934
+ const node = await hydrator(path7, value);
6935
+ return {
6936
+ node,
6937
+ cursor: btoa(cursor)
6938
+ };
6939
+ } catch (error) {
6940
+ console.log(error);
6941
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6942
+ throw new TinaQueryError({
6943
+ originalError: error,
6944
+ file: path7,
6945
+ collection: collection.name
6946
+ });
6947
+ }
6948
+ throw error;
6290
6949
  }
6291
- throw error;
6292
6950
  }
6293
- }),
6951
+ ),
6294
6952
  pageInfo: {
6295
6953
  hasPreviousPage,
6296
6954
  hasNextPage,
@@ -6315,7 +6973,7 @@ var Database = class {
6315
6973
  try {
6316
6974
  lookup = lookupFromLockFile || JSON.parse(
6317
6975
  await this.bridge.get(
6318
- (0, import_schema_tools3.normalizePath)(
6976
+ (0, import_schema_tools4.normalizePath)(
6319
6977
  import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")
6320
6978
  )
6321
6979
  )
@@ -6340,15 +6998,15 @@ var Database = class {
6340
6998
  }
6341
6999
  const contentRootLevel = nextLevel.sublevel(CONTENT_ROOT_PREFIX, SUBLEVEL_OPTIONS);
6342
7000
  await contentRootLevel.put(
6343
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_graphql.json")),
7001
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_graphql.json")),
6344
7002
  graphQLSchema
6345
7003
  );
6346
7004
  await contentRootLevel.put(
6347
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_schema.json")),
7005
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_schema.json")),
6348
7006
  tinaSchema.schema
6349
7007
  );
6350
7008
  await contentRootLevel.put(
6351
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")),
7009
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")),
6352
7010
  lookup
6353
7011
  );
6354
7012
  const result = await this._indexAllContent(
@@ -6414,13 +7072,14 @@ var Database = class {
6414
7072
  documentPaths,
6415
7073
  async (collection, documentPaths2) => {
6416
7074
  if (collection && !collection.isDetached) {
6417
- await _indexContent(
6418
- this,
6419
- this.contentLevel,
6420
- documentPaths2,
7075
+ await _indexContent({
7076
+ database: this,
7077
+ level: this.contentLevel,
7078
+ documentPaths: documentPaths2,
6421
7079
  enqueueOps,
6422
- collection
6423
- );
7080
+ collection,
7081
+ isPartialReindex: true
7082
+ });
6424
7083
  }
6425
7084
  }
6426
7085
  );
@@ -6436,17 +7095,18 @@ var Database = class {
6436
7095
  throw new Error(`No collection found for path: ${filepath}`);
6437
7096
  }
6438
7097
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
6439
- const collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
7098
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
7099
+ const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6440
7100
  let level = this.contentLevel;
6441
- if (collection == null ? void 0 : collection.isDetached) {
6442
- level = this.appLevel.sublevel(collection == null ? void 0 : collection.name, SUBLEVEL_OPTIONS);
7101
+ if (collection?.isDetached) {
7102
+ level = this.appLevel.sublevel(collection?.name, SUBLEVEL_OPTIONS);
6443
7103
  }
6444
- const itemKey = (0, import_schema_tools3.normalizePath)(filepath);
7104
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
6445
7105
  const rootSublevel = level.sublevel(
6446
7106
  CONTENT_ROOT_PREFIX,
6447
7107
  SUBLEVEL_OPTIONS
6448
7108
  );
6449
- const item = await rootSublevel.get(itemKey);
7109
+ const item = await rootSublevel.get(normalizedPath);
6450
7110
  if (item) {
6451
7111
  const folderTreeBuilder = new FolderTreeBuilder();
6452
7112
  const folderKey = folderTreeBuilder.update(
@@ -6454,16 +7114,25 @@ var Database = class {
6454
7114
  collection.path || ""
6455
7115
  );
6456
7116
  await this.contentLevel.batch([
7117
+ ...makeRefOpsForDocument(
7118
+ normalizedPath,
7119
+ collection.name,
7120
+ collectionReferences,
7121
+ item,
7122
+ "del",
7123
+ level
7124
+ ),
6457
7125
  ...makeIndexOpsForDocument(
6458
- filepath,
7126
+ normalizedPath,
6459
7127
  collection.name,
6460
7128
  collectionIndexDefinitions,
6461
7129
  item,
6462
7130
  "del",
6463
7131
  level
6464
7132
  ),
7133
+ // folder indices
6465
7134
  ...makeIndexOpsForDocument(
6466
- filepath,
7135
+ normalizedPath,
6467
7136
  `${collection.name}_${folderKey}`,
6468
7137
  collectionIndexDefinitions,
6469
7138
  item,
@@ -6472,17 +7141,17 @@ var Database = class {
6472
7141
  ),
6473
7142
  {
6474
7143
  type: "del",
6475
- key: itemKey,
7144
+ key: normalizedPath,
6476
7145
  sublevel: rootSublevel
6477
7146
  }
6478
7147
  ]);
6479
7148
  }
6480
- if (!(collection == null ? void 0 : collection.isDetached)) {
7149
+ if (!collection?.isDetached) {
6481
7150
  if (this.bridge) {
6482
- await this.bridge.delete((0, import_schema_tools3.normalizePath)(filepath));
7151
+ await this.bridge.delete(normalizedPath);
6483
7152
  }
6484
7153
  try {
6485
- await this.onDelete((0, import_schema_tools3.normalizePath)(filepath));
7154
+ await this.onDelete(normalizedPath);
6486
7155
  } catch (e) {
6487
7156
  throw new import_graphql6.GraphQLError(
6488
7157
  `Error running onDelete hook for ${filepath}: ${e}`,
@@ -6517,20 +7186,26 @@ var Database = class {
6517
7186
  );
6518
7187
  const doc = await level2.keys({ limit: 1 }).next();
6519
7188
  if (!doc) {
6520
- await _indexContent(
6521
- this,
6522
- level2,
6523
- contentPaths,
7189
+ await _indexContent({
7190
+ database: this,
7191
+ level: level2,
7192
+ documentPaths: contentPaths,
6524
7193
  enqueueOps,
6525
7194
  collection,
6526
- userFields.map((field) => [
7195
+ passwordFields: userFields.map((field) => [
6527
7196
  ...field.path,
6528
7197
  field.passwordFieldName
6529
7198
  ])
6530
- );
7199
+ });
6531
7200
  }
6532
7201
  } else {
6533
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7202
+ await _indexContent({
7203
+ database: this,
7204
+ level,
7205
+ documentPaths: contentPaths,
7206
+ enqueueOps,
7207
+ collection
7208
+ });
6534
7209
  }
6535
7210
  }
6536
7211
  );
@@ -6566,7 +7241,7 @@ var Database = class {
6566
7241
  );
6567
7242
  }
6568
7243
  const metadata = await metadataLevel.get("metadata");
6569
- return metadata == null ? void 0 : metadata.version;
7244
+ return metadata?.version;
6570
7245
  }
6571
7246
  async initLevel() {
6572
7247
  if (this.contentLevel) {
@@ -6616,6 +7291,9 @@ var Database = class {
6616
7291
  info: templateInfo
6617
7292
  };
6618
7293
  }
7294
+ /**
7295
+ * Clears the internal cache of the tinaSchema and the lookup file. This allows the state to be reset
7296
+ */
6619
7297
  clearCache() {
6620
7298
  this.tinaSchema = null;
6621
7299
  this._lookup = null;
@@ -6640,7 +7318,7 @@ var hashPasswordVisitor = async (node, path7) => {
6640
7318
  const passwordValuePath = [...path7, "value"];
6641
7319
  const plaintextPassword = get(node, passwordValuePath);
6642
7320
  if (plaintextPassword) {
6643
- (0, import_lodash5.default)(
7321
+ (0, import_compat2.set)(
6644
7322
  node,
6645
7323
  passwordValuePath,
6646
7324
  await generatePasswordHash({ password: plaintextPassword })
@@ -6649,7 +7327,7 @@ var hashPasswordVisitor = async (node, path7) => {
6649
7327
  };
6650
7328
  var visitNodes = async (node, path7, callback) => {
6651
7329
  const [currentLevel, ...remainingLevels] = path7;
6652
- if (!(remainingLevels == null ? void 0 : remainingLevels.length)) {
7330
+ if (!remainingLevels?.length) {
6653
7331
  return callback(node, path7);
6654
7332
  }
6655
7333
  if (Array.isArray(node[currentLevel])) {
@@ -6665,18 +7343,27 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
6665
7343
  async (passwordField) => visitNodes(data, passwordField, hashPasswordVisitor)
6666
7344
  )
6667
7345
  );
6668
- var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${(collection == null ? void 0 : collection.format) || "md"}`);
6669
- var _indexContent = async (database, level, documentPaths, enqueueOps, collection, passwordFields) => {
7346
+ var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${collection?.format || "md"}`);
7347
+ var _indexContent = async ({
7348
+ database,
7349
+ level,
7350
+ documentPaths,
7351
+ enqueueOps,
7352
+ collection,
7353
+ passwordFields,
7354
+ isPartialReindex
7355
+ }) => {
6670
7356
  let collectionIndexDefinitions;
6671
7357
  let collectionPath;
6672
7358
  if (collection) {
6673
7359
  const indexDefinitions = await database.getIndexDefinitions(level);
6674
- collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
7360
+ collectionIndexDefinitions = indexDefinitions?.[collection.name];
6675
7361
  if (!collectionIndexDefinitions) {
6676
7362
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6677
7363
  }
6678
7364
  collectionPath = collection.path;
6679
7365
  }
7366
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
6680
7367
  const tinaSchema = await database.getSchema();
6681
7368
  let templateInfo = null;
6682
7369
  if (collection) {
@@ -6694,27 +7381,77 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6694
7381
  if (!aliasedData) {
6695
7382
  return;
6696
7383
  }
6697
- if (passwordFields == null ? void 0 : passwordFields.length) {
7384
+ if (passwordFields?.length) {
6698
7385
  await hashPasswordValues(aliasedData, passwordFields);
6699
7386
  }
6700
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
7387
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
7388
+ const rootSublevel = level.sublevel(
7389
+ CONTENT_ROOT_PREFIX,
7390
+ SUBLEVEL_OPTIONS
7391
+ );
6701
7392
  const folderKey = folderTreeBuilder.update(
6702
7393
  normalizedPath,
6703
7394
  collectionPath || ""
6704
7395
  );
7396
+ if (isPartialReindex) {
7397
+ const item = await rootSublevel.get(normalizedPath);
7398
+ if (item) {
7399
+ await database.contentLevel.batch([
7400
+ ...makeRefOpsForDocument(
7401
+ normalizedPath,
7402
+ collection?.name,
7403
+ collectionReferences,
7404
+ item,
7405
+ "del",
7406
+ level
7407
+ ),
7408
+ ...makeIndexOpsForDocument(
7409
+ normalizedPath,
7410
+ collection.name,
7411
+ collectionIndexDefinitions,
7412
+ item,
7413
+ "del",
7414
+ level
7415
+ ),
7416
+ // folder indices
7417
+ ...makeIndexOpsForDocument(
7418
+ normalizedPath,
7419
+ `${collection.name}_${folderKey}`,
7420
+ collectionIndexDefinitions,
7421
+ item,
7422
+ "del",
7423
+ level
7424
+ ),
7425
+ {
7426
+ type: "del",
7427
+ key: normalizedPath,
7428
+ sublevel: rootSublevel
7429
+ }
7430
+ ]);
7431
+ }
7432
+ }
6705
7433
  if (!isGitKeep(filepath, collection)) {
6706
7434
  await enqueueOps([
7435
+ ...makeRefOpsForDocument(
7436
+ normalizedPath,
7437
+ collection?.name,
7438
+ collectionReferences,
7439
+ aliasedData,
7440
+ "put",
7441
+ level
7442
+ ),
6707
7443
  ...makeIndexOpsForDocument(
6708
7444
  normalizedPath,
6709
- collection == null ? void 0 : collection.name,
7445
+ collection?.name,
6710
7446
  collectionIndexDefinitions,
6711
7447
  aliasedData,
6712
7448
  "put",
6713
7449
  level
6714
7450
  ),
7451
+ // folder indexes
6715
7452
  ...makeIndexOpsForDocument(
6716
7453
  normalizedPath,
6717
- `${collection == null ? void 0 : collection.name}_${folderKey}`,
7454
+ `${collection?.name}_${folderKey}`,
6718
7455
  collectionIndexDefinitions,
6719
7456
  aliasedData,
6720
7457
  "put",
@@ -6735,8 +7472,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6735
7472
  throw new TinaFetchError(`Unable to seed ${filepath}`, {
6736
7473
  originalError: error,
6737
7474
  file: filepath,
6738
- collection: collection == null ? void 0 : collection.name,
6739
- stack: error.stack
7475
+ collection: collection?.name
6740
7476
  });
6741
7477
  }
6742
7478
  });
@@ -6761,11 +7497,12 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6761
7497
  const indexDefinitions = await database.getIndexDefinitions(
6762
7498
  database.contentLevel
6763
7499
  );
6764
- collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
7500
+ collectionIndexDefinitions = indexDefinitions?.[collection.name];
6765
7501
  if (!collectionIndexDefinitions) {
6766
7502
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6767
7503
  }
6768
7504
  }
7505
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
6769
7506
  const tinaSchema = await database.getSchema();
6770
7507
  let templateInfo = null;
6771
7508
  if (collection) {
@@ -6777,18 +7514,26 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6777
7514
  );
6778
7515
  const folderTreeBuilder = new FolderTreeBuilder();
6779
7516
  await sequential(documentPaths, async (filepath) => {
6780
- const itemKey = (0, import_schema_tools3.normalizePath)(filepath);
7517
+ const itemKey = (0, import_schema_tools4.normalizePath)(filepath);
6781
7518
  const item = await rootLevel.get(itemKey);
6782
7519
  if (item) {
6783
7520
  const folderKey = folderTreeBuilder.update(
6784
7521
  itemKey,
6785
- (collection == null ? void 0 : collection.path) || ""
7522
+ collection?.path || ""
6786
7523
  );
6787
7524
  const aliasedData = templateInfo ? replaceNameOverrides(
6788
7525
  getTemplateForFile(templateInfo, item),
6789
7526
  item
6790
7527
  ) : item;
6791
7528
  await enqueueOps([
7529
+ ...makeRefOpsForDocument(
7530
+ itemKey,
7531
+ collection?.name,
7532
+ collectionReferences,
7533
+ aliasedData,
7534
+ "del",
7535
+ database.contentLevel
7536
+ ),
6792
7537
  ...makeIndexOpsForDocument(
6793
7538
  itemKey,
6794
7539
  collection.name,
@@ -6797,9 +7542,10 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6797
7542
  "del",
6798
7543
  database.contentLevel
6799
7544
  ),
7545
+ // folder indexes
6800
7546
  ...makeIndexOpsForDocument(
6801
7547
  itemKey,
6802
- `${collection == null ? void 0 : collection.name}_${folderKey}`,
7548
+ `${collection?.name}_${folderKey}`,
6803
7549
  collectionIndexDefinitions,
6804
7550
  aliasedData,
6805
7551
  "del",
@@ -6863,14 +7609,14 @@ var getChangedFiles = async ({
6863
7609
  const rootDir = await findGitRoot(dir);
6864
7610
  let pathPrefix = "";
6865
7611
  if (rootDir !== dir) {
6866
- pathPrefix = (0, import_schema_tools3.normalizePath)(dir.substring(rootDir.length + 1));
7612
+ pathPrefix = (0, import_schema_tools4.normalizePath)(dir.substring(rootDir.length + 1));
6867
7613
  }
6868
7614
  await import_isomorphic_git.default.walk({
6869
7615
  fs: fs4,
6870
7616
  dir: rootDir,
6871
7617
  trees: [import_isomorphic_git.default.TREE({ ref: from }), import_isomorphic_git.default.TREE({ ref: to })],
6872
7618
  map: async function(filename, [A, B]) {
6873
- const relativePath = (0, import_schema_tools3.normalizePath)(filename).substring(pathPrefix.length);
7619
+ const relativePath = (0, import_schema_tools4.normalizePath)(filename).substring(pathPrefix.length);
6874
7620
  let matches = false;
6875
7621
  for (const [key, matcher] of Object.entries(pathFilter)) {
6876
7622
  if (relativePath.startsWith(key)) {
@@ -6884,12 +7630,12 @@ var getChangedFiles = async ({
6884
7630
  }
6885
7631
  }
6886
7632
  }
6887
- if (await (B == null ? void 0 : B.type()) === "tree") {
7633
+ if (await B?.type() === "tree") {
6888
7634
  return;
6889
7635
  }
6890
7636
  if (matches) {
6891
- const oidA = await (A == null ? void 0 : A.oid());
6892
- const oidB = await (B == null ? void 0 : B.oid());
7637
+ const oidA = await A?.oid();
7638
+ const oidB = await B?.oid();
6893
7639
  if (oidA !== oidB) {
6894
7640
  if (oidA === void 0) {
6895
7641
  results.added.push(relativePath);
@@ -6917,8 +7663,8 @@ var import_path5 = __toESM(require("path"));
6917
7663
  var import_normalize_path = __toESM(require("normalize-path"));
6918
7664
  var FilesystemBridge = class {
6919
7665
  constructor(rootPath, outputPath) {
6920
- this.rootPath = rootPath || "";
6921
- this.outputPath = outputPath || rootPath;
7666
+ this.rootPath = import_path5.default.resolve(rootPath);
7667
+ this.outputPath = outputPath ? import_path5.default.resolve(outputPath) : this.rootPath;
6922
7668
  }
6923
7669
  async glob(pattern, extension) {
6924
7670
  const basePath = import_path5.default.join(this.outputPath, ...pattern.split("/"));
@@ -6930,19 +7676,19 @@ var FilesystemBridge = class {
6930
7676
  }
6931
7677
  );
6932
7678
  const posixRootPath = (0, import_normalize_path.default)(this.outputPath);
6933
- return items.map((item) => {
6934
- return item.replace(posixRootPath, "").replace(/^\/|\/$/g, "");
6935
- });
7679
+ return items.map(
7680
+ (item) => item.substring(posixRootPath.length).replace(/^\/|\/$/g, "")
7681
+ );
6936
7682
  }
6937
7683
  async delete(filepath) {
6938
7684
  await import_fs_extra2.default.remove(import_path5.default.join(this.outputPath, filepath));
6939
7685
  }
6940
7686
  async get(filepath) {
6941
- return import_fs_extra2.default.readFileSync(import_path5.default.join(this.outputPath, filepath)).toString();
7687
+ return (await import_fs_extra2.default.readFile(import_path5.default.join(this.outputPath, filepath))).toString();
6942
7688
  }
6943
7689
  async put(filepath, data, basePathOverride) {
6944
7690
  const basePath = basePathOverride || this.outputPath;
6945
- await import_fs_extra2.default.outputFileSync(import_path5.default.join(basePath, filepath), data);
7691
+ await import_fs_extra2.default.outputFile(import_path5.default.join(basePath, filepath), data);
6946
7692
  }
6947
7693
  };
6948
7694
  var AuditFileSystemBridge = class extends FilesystemBridge {
@@ -7012,17 +7758,26 @@ var IsomorphicBridge = class {
7012
7758
  getAuthor() {
7013
7759
  return {
7014
7760
  ...this.author,
7015
- timestamp: Math.round(new Date().getTime() / 1e3),
7761
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
7016
7762
  timezoneOffset: 0
7017
7763
  };
7018
7764
  }
7019
7765
  getCommitter() {
7020
7766
  return {
7021
7767
  ...this.committer,
7022
- timestamp: Math.round(new Date().getTime() / 1e3),
7768
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
7023
7769
  timezoneOffset: 0
7024
7770
  };
7025
7771
  }
7772
+ /**
7773
+ * Recursively populate paths matching `pattern` for the given `entry`
7774
+ *
7775
+ * @param pattern - pattern to filter paths by
7776
+ * @param entry - TreeEntry to start building list from
7777
+ * @param path - base path
7778
+ * @param results
7779
+ * @private
7780
+ */
7026
7781
  async listEntries({
7027
7782
  pattern,
7028
7783
  entry,
@@ -7055,6 +7810,15 @@ var IsomorphicBridge = class {
7055
7810
  });
7056
7811
  }
7057
7812
  }
7813
+ /**
7814
+ * For the specified path, returns an object with an array containing the parts of the path (pathParts)
7815
+ * and an array containing the WalkerEntry objects for the path parts (pathEntries). Any null elements in the
7816
+ * pathEntries are placeholders for non-existent entries.
7817
+ *
7818
+ * @param path - path being resolved
7819
+ * @param ref - ref to resolve path entries for
7820
+ * @private
7821
+ */
7058
7822
  async resolvePathEntries(path7, ref) {
7059
7823
  let pathParts = path7.split("/");
7060
7824
  const result = await import_isomorphic_git2.default.walk({
@@ -7085,6 +7849,17 @@ var IsomorphicBridge = class {
7085
7849
  }
7086
7850
  return { pathParts, pathEntries };
7087
7851
  }
7852
+ /**
7853
+ * Updates tree entry and associated parent tree entries
7854
+ *
7855
+ * @param existingOid - the existing OID
7856
+ * @param updatedOid - the updated OID
7857
+ * @param path - the path of the entry being updated
7858
+ * @param type - the type of the entry being updated (blob or tree)
7859
+ * @param pathEntries - parent path entries
7860
+ * @param pathParts - parent path parts
7861
+ * @private
7862
+ */
7088
7863
  async updateTreeHierarchy(existingOid, updatedOid, path7, type, pathEntries, pathParts) {
7089
7864
  const lastIdx = pathEntries.length - 1;
7090
7865
  const parentEntry = pathEntries[lastIdx];
@@ -7140,6 +7915,13 @@ var IsomorphicBridge = class {
7140
7915
  );
7141
7916
  }
7142
7917
  }
7918
+ /**
7919
+ * Creates a commit for the specified tree and updates the specified ref to point to the commit
7920
+ *
7921
+ * @param treeSha - sha of the new tree
7922
+ * @param ref - the ref that should be updated
7923
+ * @private
7924
+ */
7143
7925
  async commitTree(treeSha, ref) {
7144
7926
  const commitSha = await import_isomorphic_git2.default.writeCommit({
7145
7927
  ...this.isomorphicConfig,
@@ -7152,6 +7934,7 @@ var IsomorphicBridge = class {
7152
7934
  })
7153
7935
  ],
7154
7936
  message: this.commitMessage,
7937
+ // TODO these should be configurable
7155
7938
  author: this.getAuthor(),
7156
7939
  committer: this.getCommitter()
7157
7940
  }
@@ -7390,5 +8173,5 @@ var buildSchema = async (config, flags) => {
7390
8173
  transformDocument,
7391
8174
  transformDocumentIntoPayload
7392
8175
  });
7393
- //! Replaces _.flattenDeep()
7394
8176
  //! Replaces _.get()
8177
+ //! Replaces _.flattenDeep()