@tinacms/graphql 0.0.0-e0ddb8c-20241004065742 → 0.0.0-e5c0e91-20250421003142

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,7 +62,7 @@ __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");
@@ -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];
@@ -1019,8 +1052,7 @@ var astBuilder = {
1019
1052
  }
1020
1053
  };
1021
1054
  var capitalize = (s) => {
1022
- if (typeof s !== "string")
1023
- return "";
1055
+ if (typeof s !== "string") return "";
1024
1056
  return s.charAt(0).toUpperCase() + s.slice(1);
1025
1057
  };
1026
1058
  var extractInlineTypes = (item) => {
@@ -1063,41 +1095,6 @@ function* walk(maybeNode, visited = /* @__PURE__ */ new WeakSet()) {
1063
1095
  yield maybeNode;
1064
1096
  visited.add(maybeNode);
1065
1097
  }
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
1098
  var generateNamespacedFieldName = (names, suffix = "") => {
1102
1099
  return (suffix ? [...names, suffix] : names).map(capitalize).join("");
1103
1100
  };
@@ -1257,6 +1254,11 @@ var scalarDefinitions = [
1257
1254
  required: true,
1258
1255
  type: astBuilder.TYPES.String
1259
1256
  }),
1257
+ astBuilder.FieldDefinition({
1258
+ name: "hasReferences",
1259
+ required: false,
1260
+ type: astBuilder.TYPES.Boolean
1261
+ }),
1260
1262
  astBuilder.FieldDefinition({
1261
1263
  name: "breadcrumbs",
1262
1264
  required: true,
@@ -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,6 +1929,20 @@ 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
1947
  var _a;
1790
1948
  const selections = [];
@@ -1868,9 +2026,9 @@ var Builder = class {
1868
2026
  ]
1869
2027
  });
1870
2028
  }
2029
+ // TODO: Should we throw here?
1871
2030
  case "reference":
1872
- if (depth >= this.maxDepth)
1873
- return false;
2031
+ if (depth >= this.maxDepth) return false;
1874
2032
  if (!("collections" in field)) {
1875
2033
  return false;
1876
2034
  }
@@ -1902,6 +2060,7 @@ var Builder = class {
1902
2060
  name: field.name,
1903
2061
  selections: [
1904
2062
  ...selections,
2063
+ // This is ... on Document { id }
1905
2064
  {
1906
2065
  kind: "InlineFragment",
1907
2066
  typeCondition: {
@@ -1932,6 +2091,19 @@ var Builder = class {
1932
2091
  });
1933
2092
  }
1934
2093
  };
2094
+ /**
2095
+ * ```graphql
2096
+ * # ex.
2097
+ * mutation {
2098
+ * updatePostDocument(relativePath: $relativePath, params: $params) {
2099
+ * id
2100
+ * data {...}
2101
+ * }
2102
+ * }
2103
+ * ```
2104
+ *
2105
+ * @param collection
2106
+ */
1935
2107
  this.updateCollectionDocumentMutation = async (collection) => {
1936
2108
  return astBuilder.FieldDefinition({
1937
2109
  type: await this._buildCollectionDocumentType(collection),
@@ -1951,6 +2123,19 @@ var Builder = class {
1951
2123
  ]
1952
2124
  });
1953
2125
  };
2126
+ /**
2127
+ * ```graphql
2128
+ * # ex.
2129
+ * mutation {
2130
+ * createPostDocument(relativePath: $relativePath, params: $params) {
2131
+ * id
2132
+ * data {...}
2133
+ * }
2134
+ * }
2135
+ * ```
2136
+ *
2137
+ * @param collection
2138
+ */
1954
2139
  this.createCollectionDocumentMutation = async (collection) => {
1955
2140
  return astBuilder.FieldDefinition({
1956
2141
  type: await this._buildCollectionDocumentType(collection),
@@ -1970,6 +2155,22 @@ var Builder = class {
1970
2155
  ]
1971
2156
  });
1972
2157
  };
2158
+ /**
2159
+ * ```graphql
2160
+ * # ex.
2161
+ * {
2162
+ * getPostList(first: 10) {
2163
+ * edges {
2164
+ * node {
2165
+ * id
2166
+ * }
2167
+ * }
2168
+ * }
2169
+ * }
2170
+ * ```
2171
+ *
2172
+ * @param collection
2173
+ */
1973
2174
  this.collectionDocumentList = async (collection) => {
1974
2175
  const connectionName = NAMER.referenceConnectionType(collection.namespace);
1975
2176
  this.addToLookupMap({
@@ -1985,6 +2186,10 @@ var Builder = class {
1985
2186
  collection
1986
2187
  });
1987
2188
  };
2189
+ /**
2190
+ * GraphQL type definitions which remain unchanged regardless
2191
+ * of the supplied Tina schema. Ex. "node" interface
2192
+ */
1988
2193
  this.buildStaticDefinitions = () => staticDefinitions;
1989
2194
  this._buildCollectionDocumentType = async (collection, suffix = "", extraFields = [], extraInterfaces = []) => {
1990
2195
  const documentTypeName = NAMER.documentTypeName(collection.namespace);
@@ -2489,6 +2694,7 @@ var Builder = class {
2489
2694
  name: NAMER.dataFilterTypeName(namespace),
2490
2695
  fields: await sequential(collections, async (collection2) => {
2491
2696
  return astBuilder.InputValueDefinition({
2697
+ // @ts-ignore
2492
2698
  name: collection2.name,
2493
2699
  type: NAMER.dataFilterTypeName(collection2.namespace)
2494
2700
  });
@@ -2678,7 +2884,8 @@ Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2678
2884
  });
2679
2885
  };
2680
2886
  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;
2887
+ this.maxDepth = // @ts-ignore
2888
+ (_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;
2682
2889
  this.tinaSchema = config.tinaSchema;
2683
2890
  this.lookupMap = {};
2684
2891
  }
@@ -2689,8 +2896,7 @@ Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2689
2896
  selections.push(field);
2690
2897
  });
2691
2898
  const filteredSelections = filterSelections(selections);
2692
- if (!filteredSelections.length)
2693
- return false;
2899
+ if (!filteredSelections.length) return false;
2694
2900
  return astBuilder.InlineFragmentDefinition({
2695
2901
  selections: filteredSelections,
2696
2902
  name: NAMER.dataTypeName(template.namespace)
@@ -2724,12 +2930,13 @@ var filterSelections = (arr) => {
2724
2930
  };
2725
2931
 
2726
2932
  // src/schema/createSchema.ts
2727
- var import_schema_tools2 = require("@tinacms/schema-tools");
2933
+ var import_schema_tools3 = require("@tinacms/schema-tools");
2728
2934
 
2729
2935
  // src/schema/validate.ts
2936
+ var import_schema_tools = require("@tinacms/schema-tools");
2730
2937
  var import_lodash2 = __toESM(require("lodash.clonedeep"));
2731
2938
  var yup2 = __toESM(require("yup"));
2732
- var import_schema_tools = require("@tinacms/schema-tools");
2939
+ var import_schema_tools2 = require("@tinacms/schema-tools");
2733
2940
  var FIELD_TYPES = [
2734
2941
  "string",
2735
2942
  "number",
@@ -2742,7 +2949,7 @@ var FIELD_TYPES = [
2742
2949
  "password"
2743
2950
  ];
2744
2951
  var validateSchema = async (schema) => {
2745
- const schema2 = addNamespaceToSchema(
2952
+ const schema2 = (0, import_schema_tools.addNamespaceToSchema)(
2746
2953
  (0, import_lodash2.default)(schema)
2747
2954
  );
2748
2955
  const collections = await sequential(
@@ -2751,7 +2958,7 @@ var validateSchema = async (schema) => {
2751
2958
  );
2752
2959
  validationCollectionsPathAndMatch(collections);
2753
2960
  if (schema2.config) {
2754
- const config = (0, import_schema_tools.validateTinaCloudSchemaConfig)(schema2.config);
2961
+ const config = (0, import_schema_tools2.validateTinaCloudSchemaConfig)(schema2.config);
2755
2962
  return {
2756
2963
  collections,
2757
2964
  config
@@ -2771,6 +2978,7 @@ var validationCollectionsPathAndMatch = (collections) => {
2771
2978
  }).map((x) => `${x.path}${x.format || "md"}`);
2772
2979
  if (noMatchCollections.length !== new Set(noMatchCollections).size) {
2773
2980
  throw new Error(
2981
+ // TODO: add a link to the docs
2774
2982
  "Two collections without match can not have the same `path`. Please make the `path` unique or add a matches property to the collection."
2775
2983
  );
2776
2984
  }
@@ -2882,7 +3090,7 @@ var validateField = async (field) => {
2882
3090
  // package.json
2883
3091
  var package_default = {
2884
3092
  name: "@tinacms/graphql",
2885
- version: "1.5.4",
3093
+ version: "1.5.16",
2886
3094
  main: "dist/index.js",
2887
3095
  module: "dist/index.mjs",
2888
3096
  typings: "dist/index.d.ts",
@@ -2909,8 +3117,8 @@ var package_default = {
2909
3117
  build: "tinacms-scripts build",
2910
3118
  docs: "pnpm typedoc",
2911
3119
  serve: "pnpm nodemon dist/server.js",
2912
- test: "jest",
2913
- "test-watch": "jest --watch"
3120
+ test: "vitest run",
3121
+ "test-watch": "vitest"
2914
3122
  },
2915
3123
  dependencies: {
2916
3124
  "@iarna/toml": "^2.2.5",
@@ -2918,22 +3126,22 @@ var package_default = {
2918
3126
  "@tinacms/schema-tools": "workspace:*",
2919
3127
  "abstract-level": "^1.0.4",
2920
3128
  "date-fns": "^2.30.0",
2921
- "fast-glob": "^3.3.2",
2922
- "fs-extra": "^11.2.0",
3129
+ "fast-glob": "^3.3.3",
3130
+ "fs-extra": "^11.3.0",
2923
3131
  "glob-parent": "^6.0.2",
2924
3132
  graphql: "15.8.0",
2925
3133
  "gray-matter": "^4.0.3",
2926
- "isomorphic-git": "^1.27.1",
3134
+ "isomorphic-git": "^1.29.0",
2927
3135
  "js-sha1": "^0.6.0",
2928
3136
  "js-yaml": "^3.14.1",
2929
- "jsonpath-plus": "^6.0.1",
3137
+ "jsonpath-plus": "10.1.0",
2930
3138
  "lodash.clonedeep": "^4.5.0",
2931
3139
  "lodash.set": "^4.3.2",
2932
3140
  "lodash.uniqby": "^4.7.0",
2933
3141
  "many-level": "^2.0.0",
2934
3142
  micromatch: "4.0.8",
2935
3143
  "normalize-path": "^3.0.0",
2936
- "readable-stream": "^4.5.2",
3144
+ "readable-stream": "^4.7.0",
2937
3145
  scmp: "^2.1.0",
2938
3146
  yup: "^0.32.11"
2939
3147
  },
@@ -2951,24 +3159,23 @@ var package_default = {
2951
3159
  "@types/estree": "^0.0.50",
2952
3160
  "@types/express": "^4.17.21",
2953
3161
  "@types/fs-extra": "^9.0.13",
2954
- "@types/jest": "^26.0.24",
2955
3162
  "@types/js-yaml": "^3.12.10",
2956
3163
  "@types/lodash.camelcase": "^4.3.9",
2957
3164
  "@types/lodash.upperfirst": "^4.3.9",
2958
3165
  "@types/lru-cache": "^5.1.1",
2959
3166
  "@types/mdast": "^3.0.15",
2960
3167
  "@types/micromatch": "^4.0.9",
2961
- "@types/node": "^22.7.4",
3168
+ "@types/node": "^22.13.1",
2962
3169
  "@types/normalize-path": "^3.0.2",
2963
3170
  "@types/ws": "^7.4.7",
2964
3171
  "@types/yup": "^0.29.14",
2965
- jest: "^29.7.0",
2966
- "jest-diff": "^29.7.0",
2967
3172
  "jest-file-snapshot": "^0.5.0",
2968
- "jest-matcher-utils": "^29.7.0",
2969
3173
  "memory-level": "^1.0.0",
2970
3174
  nodemon: "3.1.4",
2971
- typescript: "^5.6.2"
3175
+ typescript: "^5.7.3",
3176
+ vite: "^4.5.9",
3177
+ vitest: "^0.32.4",
3178
+ zod: "^3.24.2"
2972
3179
  }
2973
3180
  };
2974
3181
 
@@ -2983,7 +3190,7 @@ var createSchema = async ({
2983
3190
  if (flags && flags.length > 0) {
2984
3191
  meta["flags"] = flags;
2985
3192
  }
2986
- return new import_schema_tools2.TinaSchema({
3193
+ return new import_schema_tools3.TinaSchema({
2987
3194
  version: {
2988
3195
  fullVersion: package_default.version,
2989
3196
  major,
@@ -3039,6 +3246,7 @@ var _buildFragments = async (builder, tinaSchema) => {
3039
3246
  const fragDoc = {
3040
3247
  kind: "Document",
3041
3248
  definitions: (0, import_lodash3.default)(
3249
+ // @ts-ignore
3042
3250
  extractInlineTypes(fragmentDefinitionsFields),
3043
3251
  (node) => node.name.value
3044
3252
  )
@@ -3062,6 +3270,7 @@ var _buildQueries = async (builder, tinaSchema) => {
3062
3270
  fragName,
3063
3271
  queryName: queryListName,
3064
3272
  filterType: queryFilterTypeName,
3273
+ // look for flag to see if the data layer is enabled
3065
3274
  dataLayer: Boolean(
3066
3275
  (_c = (_b = (_a = tinaSchema.config) == null ? void 0 : _a.meta) == null ? void 0 : _b.flags) == null ? void 0 : _c.find((x) => x === "experimentalData")
3067
3276
  )
@@ -3071,6 +3280,7 @@ var _buildQueries = async (builder, tinaSchema) => {
3071
3280
  const queryDoc = {
3072
3281
  kind: "Document",
3073
3282
  definitions: (0, import_lodash3.default)(
3283
+ // @ts-ignore
3074
3284
  extractInlineTypes(operationsDefinitions),
3075
3285
  (node) => node.name.value
3076
3286
  )
@@ -3122,7 +3332,9 @@ var _buildSchema = async (builder, tinaSchema) => {
3122
3332
  await builder.buildCreateCollectionFolderMutation()
3123
3333
  );
3124
3334
  await sequential(collections, async (collection) => {
3125
- queryTypeDefinitionFields.push(await builder.collectionDocument(collection));
3335
+ queryTypeDefinitionFields.push(
3336
+ await builder.collectionDocument(collection)
3337
+ );
3126
3338
  if (collection.isAuthCollection) {
3127
3339
  queryTypeDefinitionFields.push(
3128
3340
  await builder.authenticationCollectionDocument(collection)
@@ -3159,6 +3371,7 @@ var _buildSchema = async (builder, tinaSchema) => {
3159
3371
  return {
3160
3372
  kind: "Document",
3161
3373
  definitions: (0, import_lodash3.default)(
3374
+ // @ts-ignore
3162
3375
  extractInlineTypes(definitions),
3163
3376
  (node) => node.name.value
3164
3377
  )
@@ -3175,6 +3388,9 @@ var import_isValid = __toESM(require("date-fns/isValid/index.js"));
3175
3388
  // src/mdx/index.ts
3176
3389
  var import_mdx = require("@tinacms/mdx");
3177
3390
 
3391
+ // src/resolver/index.ts
3392
+ var import_jsonpath_plus2 = require("jsonpath-plus");
3393
+
3178
3394
  // src/resolver/error.ts
3179
3395
  var TinaGraphQLError = class extends Error {
3180
3396
  constructor(message, extensions) {
@@ -3360,8 +3576,7 @@ var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, s
3360
3576
  }
3361
3577
  if (Array.isArray(value)) {
3362
3578
  return value.map((v) => {
3363
- if (!v || typeof v !== "string")
3364
- return v;
3579
+ if (!v || typeof v !== "string") return v;
3365
3580
  const cleanMediaRoot = cleanUpSlashes(
3366
3581
  schema.config.media.tina.mediaRoot
3367
3582
  );
@@ -3389,8 +3604,7 @@ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, s
3389
3604
  }
3390
3605
  if (Array.isArray(value)) {
3391
3606
  return value.map((v) => {
3392
- if (!v || typeof v !== "string")
3393
- return v;
3607
+ if (!v || typeof v !== "string") return v;
3394
3608
  const strippedValue = v.replace(cleanMediaRoot, "");
3395
3609
  return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3396
3610
  });
@@ -3409,8 +3623,7 @@ var cleanUpSlashes = (path7) => {
3409
3623
  };
3410
3624
  var hasTinaMediaConfig = (schema) => {
3411
3625
  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;
3626
+ if (!((_b = (_a = schema.config) == null ? void 0 : _a.media) == null ? void 0 : _b.tina)) return false;
3414
3627
  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
3628
  return false;
3416
3629
  return true;
@@ -3437,7 +3650,9 @@ var LevelProxyHandler = {
3437
3650
  throw new Error(`The property, ${property.toString()}, doesn't exist`);
3438
3651
  }
3439
3652
  if (typeof target[property] !== "function") {
3440
- throw new Error(`The property, ${property.toString()}, is not a function`);
3653
+ throw new Error(
3654
+ `The property, ${property.toString()}, is not a function`
3655
+ );
3441
3656
  }
3442
3657
  if (property === "get") {
3443
3658
  return async (...args) => {
@@ -3454,6 +3669,7 @@ var LevelProxyHandler = {
3454
3669
  } else if (property === "sublevel") {
3455
3670
  return (...args) => {
3456
3671
  return new Proxy(
3672
+ // eslint-disable-next-line prefer-spread
3457
3673
  target[property].apply(target, args),
3458
3674
  LevelProxyHandler
3459
3675
  );
@@ -3476,7 +3692,7 @@ var import_path2 = __toESM(require("path"));
3476
3692
  var import_toml = __toESM(require("@iarna/toml"));
3477
3693
  var import_js_yaml = __toESM(require("js-yaml"));
3478
3694
  var import_gray_matter = __toESM(require("gray-matter"));
3479
- var import_schema_tools3 = require("@tinacms/schema-tools");
3695
+ var import_schema_tools4 = require("@tinacms/schema-tools");
3480
3696
  var import_micromatch = __toESM(require("micromatch"));
3481
3697
  var import_path = __toESM(require("path"));
3482
3698
 
@@ -3689,7 +3905,7 @@ var scanAllContent = async (tinaSchema, bridge, callback) => {
3689
3905
  const filesSeen = /* @__PURE__ */ new Map();
3690
3906
  const duplicateFiles = /* @__PURE__ */ new Set();
3691
3907
  await sequential(tinaSchema.getCollections(), async (collection) => {
3692
- const normalPath = (0, import_schema_tools3.normalizePath)(collection.path);
3908
+ const normalPath = (0, import_schema_tools4.normalizePath)(collection.path);
3693
3909
  const format = collection.format || "md";
3694
3910
  const documentPaths = await bridge.glob(normalPath, format);
3695
3911
  const matches = tinaSchema.getMatches({ collection });
@@ -3801,7 +4017,7 @@ var getTemplateForFile = (templateInfo, data) => {
3801
4017
  throw new Error(`Unable to determine template`);
3802
4018
  };
3803
4019
  var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo) => {
3804
- const dataString = await bridge.get((0, import_schema_tools3.normalizePath)(filepath));
4020
+ const dataString = await bridge.get((0, import_schema_tools4.normalizePath)(filepath));
3805
4021
  const data = parseFile(
3806
4022
  dataString,
3807
4023
  import_path.default.extname(filepath),
@@ -3823,6 +4039,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3823
4039
 
3824
4040
  // src/database/datalayer.ts
3825
4041
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
4042
+ var REFS_COLLECTIONS_SORT_KEY = "__refs__";
4043
+ var REFS_REFERENCE_FIELD = "__tina_ref__";
4044
+ var REFS_PATH_FIELD = "__tina_ref_path__";
3826
4045
  var DEFAULT_NUMERIC_LPAD = 4;
3827
4046
  var applyPadding = (input, pad) => {
3828
4047
  if (pad) {
@@ -4280,7 +4499,7 @@ var FolderTreeBuilder = class {
4280
4499
  return this._tree;
4281
4500
  }
4282
4501
  update(documentPath, collectionPath) {
4283
- let folderPath = import_path2.default.dirname((0, import_schema_tools3.normalizePath)(documentPath));
4502
+ let folderPath = import_path2.default.dirname((0, import_schema_tools4.normalizePath)(documentPath));
4284
4503
  if (folderPath === ".") {
4285
4504
  folderPath = "";
4286
4505
  }
@@ -4293,7 +4512,7 @@ var FolderTreeBuilder = class {
4293
4512
  if (!this._tree[current2]) {
4294
4513
  this._tree[current2] = /* @__PURE__ */ new Set();
4295
4514
  }
4296
- this._tree[current2].add((0, import_schema_tools3.normalizePath)(import_path2.default.join(current2, part)));
4515
+ this._tree[current2].add((0, import_schema_tools4.normalizePath)(import_path2.default.join(current2, part)));
4297
4516
  parent.push(part);
4298
4517
  });
4299
4518
  const current = parent.join("/");
@@ -4332,6 +4551,7 @@ var makeFolderOpsForCollection = (folderTree, collection, indexDefinitions, opTy
4332
4551
  result.push({
4333
4552
  type: opType,
4334
4553
  key: `${collection.path}/${subFolderKey}.${collection.format}`,
4554
+ // replace the root with the collection path
4335
4555
  sublevel: indexSublevel,
4336
4556
  value: {}
4337
4557
  });
@@ -4395,6 +4615,57 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4395
4615
  }
4396
4616
  return result;
4397
4617
  };
4618
+ var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4619
+ const result = [];
4620
+ if (collection) {
4621
+ for (const [c, referencePaths] of Object.entries(references || {})) {
4622
+ if (!referencePaths.length) {
4623
+ continue;
4624
+ }
4625
+ const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4626
+ const refSublevel = collectionSublevel.sublevel(
4627
+ REFS_COLLECTIONS_SORT_KEY,
4628
+ SUBLEVEL_OPTIONS
4629
+ );
4630
+ const references2 = {};
4631
+ for (const path7 of referencePaths) {
4632
+ const ref = (0, import_jsonpath_plus.JSONPath)({ path: path7, json: data });
4633
+ if (!ref) {
4634
+ continue;
4635
+ }
4636
+ if (Array.isArray(ref)) {
4637
+ for (const r of ref) {
4638
+ if (!r) {
4639
+ continue;
4640
+ }
4641
+ if (references2[r]) {
4642
+ references2[r].push(path7);
4643
+ } else {
4644
+ references2[r] = [path7];
4645
+ }
4646
+ }
4647
+ } else {
4648
+ if (references2[ref]) {
4649
+ references2[ref].push(path7);
4650
+ } else {
4651
+ references2[ref] = [path7];
4652
+ }
4653
+ }
4654
+ }
4655
+ for (const ref of Object.keys(references2)) {
4656
+ for (const path7 of references2[ref]) {
4657
+ result.push({
4658
+ type: opType,
4659
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4660
+ sublevel: refSublevel,
4661
+ value: opType === "put" ? {} : void 0
4662
+ });
4663
+ }
4664
+ }
4665
+ }
4666
+ }
4667
+ return result;
4668
+ };
4398
4669
  var makeStringEscaper = (regex, replacement) => {
4399
4670
  return (input) => {
4400
4671
  if (Array.isArray(input)) {
@@ -4447,6 +4718,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4447
4718
  case "password":
4448
4719
  accumulator[field.name] = {
4449
4720
  value: void 0,
4721
+ // never resolve the password hash
4450
4722
  passwordChangeRequired: (_a = value["passwordChangeRequired"]) != null ? _a : false
4451
4723
  };
4452
4724
  break;
@@ -4541,7 +4813,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4541
4813
  }
4542
4814
  return accumulator;
4543
4815
  };
4544
- var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit) => {
4816
+ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit, hasReferences) => {
4545
4817
  const collection = tinaSchema.getCollection(rawData._collection);
4546
4818
  try {
4547
4819
  const template = tinaSchema.getTemplateForData({
@@ -4595,6 +4867,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4595
4867
  basename,
4596
4868
  filename,
4597
4869
  extension,
4870
+ hasReferences,
4598
4871
  path: fullPath,
4599
4872
  relativePath,
4600
4873
  breadcrumbs,
@@ -4614,6 +4887,34 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4614
4887
  throw e;
4615
4888
  }
4616
4889
  };
4890
+ var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4891
+ let updated = false;
4892
+ if (!path7.includes(".") && !path7.includes("[")) {
4893
+ if (path7 in obj && obj[path7] === oldValue) {
4894
+ obj[path7] = newValue;
4895
+ updated = true;
4896
+ }
4897
+ return { object: obj, updated };
4898
+ }
4899
+ const parentPath = path7.replace(/\.[^.\[\]]+$/, "");
4900
+ const keyToUpdate = path7.match(/[^.\[\]]+$/)[0];
4901
+ const parents = (0, import_jsonpath_plus2.JSONPath)({
4902
+ path: parentPath,
4903
+ json: obj,
4904
+ resultType: "value"
4905
+ });
4906
+ if (parents.length > 0) {
4907
+ parents.forEach((parent) => {
4908
+ if (parent && typeof parent === "object" && keyToUpdate in parent) {
4909
+ if (parent[keyToUpdate] === oldValue) {
4910
+ parent[keyToUpdate] = newValue;
4911
+ updated = true;
4912
+ }
4913
+ }
4914
+ });
4915
+ }
4916
+ return { object: obj, updated };
4917
+ };
4617
4918
  var Resolver = class {
4618
4919
  constructor(init) {
4619
4920
  this.init = init;
@@ -4621,6 +4922,7 @@ var Resolver = class {
4621
4922
  const collection = this.tinaSchema.getCollection(collectionName);
4622
4923
  const extraFields = {};
4623
4924
  return {
4925
+ // return the collection and hasDocuments to resolve documents at a lower level
4624
4926
  documents: { collection, hasDocuments },
4625
4927
  ...collection,
4626
4928
  ...extraFields
@@ -4628,7 +4930,9 @@ var Resolver = class {
4628
4930
  };
4629
4931
  this.getRaw = async (fullPath) => {
4630
4932
  if (typeof fullPath !== "string") {
4631
- throw new Error(`fullPath must be of type string for getDocument request`);
4933
+ throw new Error(
4934
+ `fullPath must be of type string for getDocument request`
4935
+ );
4632
4936
  }
4633
4937
  return this.database.get(fullPath);
4634
4938
  };
@@ -4655,22 +4959,28 @@ var Resolver = class {
4655
4959
  );
4656
4960
  }
4657
4961
  };
4658
- this.getDocument = async (fullPath) => {
4962
+ this.getDocument = async (fullPath, opts = {}) => {
4659
4963
  if (typeof fullPath !== "string") {
4660
- throw new Error(`fullPath must be of type string for getDocument request`);
4964
+ throw new Error(
4965
+ `fullPath must be of type string for getDocument request`
4966
+ );
4661
4967
  }
4662
4968
  const rawData = await this.getRaw(fullPath);
4969
+ const hasReferences = (opts == null ? void 0 : opts.checkReferences) ? await this.hasReferences(fullPath, opts.collection) : void 0;
4663
4970
  return transformDocumentIntoPayload(
4664
4971
  fullPath,
4665
4972
  rawData,
4666
4973
  this.tinaSchema,
4667
4974
  this.config,
4668
- this.isAudit
4975
+ this.isAudit,
4976
+ hasReferences
4669
4977
  );
4670
4978
  };
4671
4979
  this.deleteDocument = async (fullPath) => {
4672
4980
  if (typeof fullPath !== "string") {
4673
- throw new Error(`fullPath must be of type string for getDocument request`);
4981
+ throw new Error(
4982
+ `fullPath must be of type string for getDocument request`
4983
+ );
4674
4984
  }
4675
4985
  await this.database.delete(fullPath);
4676
4986
  };
@@ -4705,7 +5015,9 @@ var Resolver = class {
4705
5015
  );
4706
5016
  } else {
4707
5017
  return this.buildFieldMutations(
5018
+ // @ts-ignore FIXME Argument of type 'string | object' is not assignable to parameter of type '{ [fieldName: string]: string | object | (string | object)[]; }'
4708
5019
  fieldValue,
5020
+ //@ts-ignore
4709
5021
  objectTemplate,
4710
5022
  existingData
4711
5023
  );
@@ -4717,6 +5029,7 @@ var Resolver = class {
4717
5029
  fieldValue.map(async (item) => {
4718
5030
  if (typeof item === "string") {
4719
5031
  throw new Error(
5032
+ //@ts-ignore
4720
5033
  `Expected object for template value for field ${field.name}`
4721
5034
  );
4722
5035
  }
@@ -4725,16 +5038,19 @@ var Resolver = class {
4725
5038
  });
4726
5039
  const [templateName] = Object.entries(item)[0];
4727
5040
  const template = templates.find(
5041
+ //@ts-ignore
4728
5042
  (template2) => template2.name === templateName
4729
5043
  );
4730
5044
  if (!template) {
4731
5045
  throw new Error(`Expected to find template ${templateName}`);
4732
5046
  }
4733
5047
  return {
5048
+ // @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
5049
  ...await this.buildFieldMutations(
4735
5050
  item[template.name],
4736
5051
  template
4737
5052
  ),
5053
+ //@ts-ignore
4738
5054
  _template: template.name
4739
5055
  };
4740
5056
  })
@@ -4742,6 +5058,7 @@ var Resolver = class {
4742
5058
  } else {
4743
5059
  if (typeof fieldValue === "string") {
4744
5060
  throw new Error(
5061
+ //@ts-ignore
4745
5062
  `Expected object for template value for field ${field.name}`
4746
5063
  );
4747
5064
  }
@@ -4750,16 +5067,19 @@ var Resolver = class {
4750
5067
  });
4751
5068
  const [templateName] = Object.entries(fieldValue)[0];
4752
5069
  const template = templates.find(
5070
+ //@ts-ignore
4753
5071
  (template2) => template2.name === templateName
4754
5072
  );
4755
5073
  if (!template) {
4756
5074
  throw new Error(`Expected to find template ${templateName}`);
4757
5075
  }
4758
5076
  return {
5077
+ // @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
5078
  ...await this.buildFieldMutations(
4760
5079
  fieldValue[template.name],
4761
5080
  template
4762
5081
  ),
5082
+ //@ts-ignore
4763
5083
  _template: template.name
4764
5084
  };
4765
5085
  }
@@ -4799,6 +5119,7 @@ var Resolver = class {
4799
5119
  return this.getDocument(realPath);
4800
5120
  }
4801
5121
  const params = await this.buildObjectMutations(
5122
+ // @ts-ignore
4802
5123
  args.params[collection.name],
4803
5124
  collection
4804
5125
  );
@@ -4844,6 +5165,7 @@ var Resolver = class {
4844
5165
  const values = {
4845
5166
  ...oldDoc,
4846
5167
  ...await this.buildFieldMutations(
5168
+ // @ts-ignore FIXME: failing on unknown, which we don't need to know because it's recursive
4847
5169
  templateParams,
4848
5170
  template,
4849
5171
  doc == null ? void 0 : doc._rawData
@@ -4857,13 +5179,22 @@ var Resolver = class {
4857
5179
  return this.getDocument(realPath);
4858
5180
  }
4859
5181
  const params = await this.buildObjectMutations(
5182
+ //@ts-ignore
4860
5183
  isCollectionSpecific ? args.params : args.params[collection.name],
4861
5184
  collection,
4862
5185
  doc == null ? void 0 : doc._rawData
4863
5186
  );
4864
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
5187
+ await this.database.put(
5188
+ realPath,
5189
+ { ...oldDoc, ...params },
5190
+ collection.name
5191
+ );
4865
5192
  return this.getDocument(realPath);
4866
5193
  };
5194
+ /**
5195
+ * Returns top-level fields which are not defined in the collection, so their
5196
+ * values are not eliminated from Tina when new values are saved
5197
+ */
4867
5198
  this.resolveLegacyValues = (oldDoc, collection) => {
4868
5199
  const legacyValues = {};
4869
5200
  Object.entries(oldDoc).forEach(([key, value]) => {
@@ -4969,6 +5300,40 @@ var Resolver = class {
4969
5300
  if (isDeletion) {
4970
5301
  const doc = await this.getDocument(realPath);
4971
5302
  await this.deleteDocument(realPath);
5303
+ if (await this.hasReferences(realPath, collection)) {
5304
+ const collRefs = await this.findReferences(realPath, collection);
5305
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5306
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5307
+ docsWithRefs
5308
+ )) {
5309
+ let refDoc = await this.getRaw(pathToDocWithRef);
5310
+ let hasUpdate = false;
5311
+ for (const path7 of referencePaths) {
5312
+ const { object: object2, updated } = updateObjectWithJsonPath(
5313
+ refDoc,
5314
+ path7,
5315
+ realPath,
5316
+ null
5317
+ );
5318
+ refDoc = object2;
5319
+ hasUpdate = updated || hasUpdate;
5320
+ }
5321
+ if (hasUpdate) {
5322
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5323
+ if (!collectionWithRef) {
5324
+ throw new Error(
5325
+ `Unable to find collection for ${pathToDocWithRef}`
5326
+ );
5327
+ }
5328
+ await this.database.put(
5329
+ pathToDocWithRef,
5330
+ refDoc,
5331
+ collectionWithRef.name
5332
+ );
5333
+ }
5334
+ }
5335
+ }
5336
+ }
4972
5337
  return doc;
4973
5338
  }
4974
5339
  if (isUpdateName) {
@@ -4985,12 +5350,49 @@ var Resolver = class {
4985
5350
  collection == null ? void 0 : collection.path,
4986
5351
  args.params.relativePath
4987
5352
  );
5353
+ if (newRealPath === realPath) {
5354
+ return doc;
5355
+ }
4988
5356
  await this.database.put(newRealPath, doc._rawData, collection.name);
4989
5357
  await this.deleteDocument(realPath);
5358
+ const collRefs = await this.findReferences(realPath, collection);
5359
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5360
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5361
+ docsWithRefs
5362
+ )) {
5363
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5364
+ let hasUpdate = false;
5365
+ for (const path7 of referencePaths) {
5366
+ const { object: object2, updated } = updateObjectWithJsonPath(
5367
+ docWithRef,
5368
+ path7,
5369
+ realPath,
5370
+ newRealPath
5371
+ );
5372
+ docWithRef = object2;
5373
+ hasUpdate = updated || hasUpdate;
5374
+ }
5375
+ if (hasUpdate) {
5376
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5377
+ if (!collectionWithRef) {
5378
+ throw new Error(
5379
+ `Unable to find collection for ${pathToDocWithRef}`
5380
+ );
5381
+ }
5382
+ await this.database.put(
5383
+ pathToDocWithRef,
5384
+ docWithRef,
5385
+ collectionWithRef.name
5386
+ );
5387
+ }
5388
+ }
5389
+ }
4990
5390
  return this.getDocument(newRealPath);
4991
5391
  }
4992
5392
  if (alreadyExists === false) {
4993
- throw new Error(`Unable to update document, ${realPath} does not exist`);
5393
+ throw new Error(
5394
+ `Unable to update document, ${realPath} does not exist`
5395
+ );
4994
5396
  }
4995
5397
  return this.updateResolveDocument({
4996
5398
  collection,
@@ -5000,7 +5402,10 @@ var Resolver = class {
5000
5402
  isCollectionSpecific
5001
5403
  });
5002
5404
  } else {
5003
- return this.getDocument(realPath);
5405
+ return this.getDocument(realPath, {
5406
+ collection,
5407
+ checkReferences: true
5408
+ });
5004
5409
  }
5005
5410
  };
5006
5411
  this.resolveCollectionConnections = async ({ ids }) => {
@@ -5037,6 +5442,7 @@ var Resolver = class {
5037
5442
  },
5038
5443
  collection: referencedCollection,
5039
5444
  hydrator: (path7) => path7
5445
+ // just return the path
5040
5446
  }
5041
5447
  );
5042
5448
  const { edges } = resolvedCollectionConnection;
@@ -5104,6 +5510,82 @@ var Resolver = class {
5104
5510
  }
5105
5511
  };
5106
5512
  };
5513
+ /**
5514
+ * Checks if a document has references to it
5515
+ * @param id The id of the document to check for references
5516
+ * @param c The collection to check for references
5517
+ * @returns true if the document has references, false otherwise
5518
+ */
5519
+ this.hasReferences = async (id, c) => {
5520
+ let count = 0;
5521
+ await this.database.query(
5522
+ {
5523
+ collection: c.name,
5524
+ filterChain: makeFilterChain({
5525
+ conditions: [
5526
+ {
5527
+ filterPath: REFS_REFERENCE_FIELD,
5528
+ filterExpression: {
5529
+ _type: "string",
5530
+ _list: false,
5531
+ eq: id
5532
+ }
5533
+ }
5534
+ ]
5535
+ }),
5536
+ sort: REFS_COLLECTIONS_SORT_KEY
5537
+ },
5538
+ (refId) => {
5539
+ count++;
5540
+ return refId;
5541
+ }
5542
+ );
5543
+ if (count) {
5544
+ return true;
5545
+ }
5546
+ return false;
5547
+ };
5548
+ /**
5549
+ * Finds references to a document
5550
+ * @param id the id of the document to find references to
5551
+ * @param c the collection to find references in
5552
+ * @returns a map of references to the document
5553
+ */
5554
+ this.findReferences = async (id, c) => {
5555
+ const references = {};
5556
+ await this.database.query(
5557
+ {
5558
+ collection: c.name,
5559
+ filterChain: makeFilterChain({
5560
+ conditions: [
5561
+ {
5562
+ filterPath: REFS_REFERENCE_FIELD,
5563
+ filterExpression: {
5564
+ _type: "string",
5565
+ _list: false,
5566
+ eq: id
5567
+ }
5568
+ }
5569
+ ]
5570
+ }),
5571
+ sort: REFS_COLLECTIONS_SORT_KEY
5572
+ },
5573
+ (refId, rawItem) => {
5574
+ if (!references[c.name]) {
5575
+ references[c.name] = {};
5576
+ }
5577
+ if (!references[c.name][refId]) {
5578
+ references[c.name][refId] = [];
5579
+ }
5580
+ const referencePath = rawItem == null ? void 0 : rawItem[REFS_PATH_FIELD];
5581
+ if (referencePath) {
5582
+ references[c.name][refId].push(referencePath);
5583
+ }
5584
+ return refId;
5585
+ }
5586
+ );
5587
+ return references;
5588
+ };
5107
5589
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
5108
5590
  var _a;
5109
5591
  const accum = {};
@@ -5191,6 +5673,27 @@ var Resolver = class {
5191
5673
  }
5192
5674
  return accum;
5193
5675
  };
5676
+ /**
5677
+ * A mutation looks nearly identical between updateDocument:
5678
+ * ```graphql
5679
+ * updateDocument(collection: $collection,relativePath: $path, params: {
5680
+ * post: {
5681
+ * title: "Hello, World"
5682
+ * }
5683
+ * })`
5684
+ * ```
5685
+ * and `updatePostDocument`:
5686
+ * ```graphql
5687
+ * updatePostDocument(relativePath: $path, params: {
5688
+ * title: "Hello, World"
5689
+ * })
5690
+ * ```
5691
+ * The problem here is that we don't know whether the payload came from `updateDocument`
5692
+ * or `updatePostDocument` (we could, but for now it's easier not to pipe those details through),
5693
+ * But we do know that when given a `args.collection` value, we can assume that
5694
+ * this was a `updateDocument` request, and thus - should grab the data
5695
+ * from the corresponding field name in the key
5696
+ */
5194
5697
  this.buildParams = (args) => {
5195
5698
  try {
5196
5699
  assertShape(
@@ -5291,7 +5794,10 @@ var resolve = async ({
5291
5794
  const graphQLSchema = (0, import_graphql5.buildASTSchema)(graphQLSchemaAst);
5292
5795
  const tinaConfig = await database.getTinaSchema();
5293
5796
  const tinaSchema = await createSchema({
5797
+ // TODO: please update all the types to import from @tinacms/schema-tools
5798
+ // @ts-ignore
5294
5799
  schema: tinaConfig,
5800
+ // @ts-ignore
5295
5801
  flags: (_a = tinaConfig == null ? void 0 : tinaConfig.meta) == null ? void 0 : _a.flags
5296
5802
  });
5297
5803
  const resolver = createResolver({
@@ -5308,8 +5814,7 @@ var resolve = async ({
5308
5814
  database
5309
5815
  },
5310
5816
  typeResolver: async (source, _args, info) => {
5311
- if (source.__typename)
5312
- return source.__typename;
5817
+ if (source.__typename) return source.__typename;
5313
5818
  const namedType = (0, import_graphql5.getNamedType)(info.returnType).toString();
5314
5819
  const lookup = await database.getLookup(namedType);
5315
5820
  if (lookup.resolveType === "unionData") {
@@ -5461,11 +5966,13 @@ var resolve = async ({
5461
5966
  (0, import_lodash4.default)(
5462
5967
  params,
5463
5968
  userField.path.slice(1),
5969
+ // remove _rawData from users path
5464
5970
  users.map((u) => {
5465
5971
  if (user[idFieldName] === u[idFieldName]) {
5466
5972
  return user;
5467
5973
  }
5468
5974
  return {
5975
+ // don't overwrite other users' passwords
5469
5976
  ...u,
5470
5977
  [passwordFieldName]: {
5471
5978
  ...u[passwordFieldName],
@@ -5488,6 +5995,9 @@ var resolve = async ({
5488
5995
  }
5489
5996
  const isCreation = lookup[info.fieldName] === "create";
5490
5997
  switch (lookup.resolveType) {
5998
+ /**
5999
+ * `node(id: $id)`
6000
+ */
5491
6001
  case "nodeDocument":
5492
6002
  assertShape(
5493
6003
  args,
@@ -5519,6 +6029,7 @@ var resolve = async ({
5519
6029
  collection: args.collection,
5520
6030
  isMutation,
5521
6031
  isCreation,
6032
+ // Right now this is the only case for deletion
5522
6033
  isDeletion: info.fieldName === "deleteDocument",
5523
6034
  isFolderCreation: info.fieldName === "createFolder",
5524
6035
  isUpdateName: Boolean((_a2 = args == null ? void 0 : args.params) == null ? void 0 : _a2.relativePath),
@@ -5528,6 +6039,9 @@ var resolve = async ({
5528
6039
  return result;
5529
6040
  }
5530
6041
  return value;
6042
+ /**
6043
+ * eg `getMovieDocument.data.actors`
6044
+ */
5531
6045
  case "multiCollectionDocumentList":
5532
6046
  if (Array.isArray(value)) {
5533
6047
  return {
@@ -5539,7 +6053,15 @@ var resolve = async ({
5539
6053
  }
5540
6054
  if (info.fieldName === "documents" && (value == null ? void 0 : value.collection) && (value == null ? void 0 : value.hasDocuments)) {
5541
6055
  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") {
6056
+ if (
6057
+ // 1. Make sure that the filter exists
6058
+ typeof (args == null ? void 0 : args.filter) !== "undefined" && (args == null ? void 0 : args.filter) !== null && // 2. Make sure that the collection name exists
6059
+ // @ts-ignore
6060
+ typeof ((_b = value == null ? void 0 : value.collection) == null ? void 0 : _b.name) === "string" && // 3. Make sure that the collection name is in the filter and is not undefined
6061
+ // @ts-ignore
6062
+ Object.keys(args.filter).includes((_c = value == null ? void 0 : value.collection) == null ? void 0 : _c.name) && // @ts-ignore
6063
+ typeof args.filter[(_d = value == null ? void 0 : value.collection) == null ? void 0 : _d.name] !== "undefined"
6064
+ ) {
5543
6065
  filter = args.filter[value.collection.name];
5544
6066
  }
5545
6067
  return resolver.resolveCollectionConnection({
@@ -5547,12 +6069,20 @@ var resolve = async ({
5547
6069
  ...args,
5548
6070
  filter
5549
6071
  },
6072
+ // @ts-ignore
5550
6073
  collection: value.collection
5551
6074
  });
5552
6075
  }
5553
6076
  throw new Error(
5554
6077
  `Expected an array for result of ${info.fieldName} at ${info.path}`
5555
6078
  );
6079
+ /**
6080
+ * Collections-specific getter
6081
+ * eg. `getPostDocument`/`createPostDocument`/`updatePostDocument`
6082
+ *
6083
+ * if coming from a query result
6084
+ * the field will be `node`
6085
+ */
5556
6086
  case "collectionDocument": {
5557
6087
  if (value) {
5558
6088
  return value;
@@ -5567,11 +6097,32 @@ var resolve = async ({
5567
6097
  });
5568
6098
  return result;
5569
6099
  }
6100
+ /**
6101
+ * Collections-specific list getter
6102
+ * eg. `getPageList`
6103
+ */
5570
6104
  case "collectionDocumentList":
5571
6105
  return resolver.resolveCollectionConnection({
5572
6106
  args,
5573
6107
  collection: tinaSchema.getCollection(lookup.collection)
5574
6108
  });
6109
+ /**
6110
+ * A polymorphic data set, it can be from a document's data
6111
+ * of any nested object which can be one of many shapes
6112
+ *
6113
+ * ```graphql
6114
+ * getPostDocument(relativePath: $relativePath) {
6115
+ * data {...} <- this part
6116
+ * }
6117
+ * ```
6118
+ * ```graphql
6119
+ * getBlockDocument(relativePath: $relativePath) {
6120
+ * data {
6121
+ * blocks {...} <- or this part
6122
+ * }
6123
+ * }
6124
+ * ```
6125
+ */
5575
6126
  case "unionData":
5576
6127
  if (!value) {
5577
6128
  if (args.relativePath) {
@@ -5636,8 +6187,7 @@ var TinaLevelClient = class extends import_many_level.ManyLevelGuest {
5636
6187
  this.port = port || 9e3;
5637
6188
  }
5638
6189
  openConnection() {
5639
- if (this._connected)
5640
- return;
6190
+ if (this._connected) return;
5641
6191
  const socket = (0, import_net.connect)(this.port);
5642
6192
  (0, import_readable_stream.pipeline)(socket, this.createRpcStream(), socket, () => {
5643
6193
  this._connected = false;
@@ -5757,7 +6307,7 @@ var Database = class {
5757
6307
  const contentObject = await level.sublevel(
5758
6308
  CONTENT_ROOT_PREFIX,
5759
6309
  SUBLEVEL_OPTIONS
5760
- ).get((0, import_schema_tools3.normalizePath)(filepath));
6310
+ ).get((0, import_schema_tools4.normalizePath)(filepath));
5761
6311
  if (!contentObject) {
5762
6312
  throw new NotFoundError(`Unable to find record ${filepath}`);
5763
6313
  }
@@ -5769,6 +6319,7 @@ var Database = class {
5769
6319
  }
5770
6320
  };
5771
6321
  this.addPendingDocument = async (filepath, data) => {
6322
+ var _a;
5772
6323
  await this.initLevel();
5773
6324
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5774
6325
  const collection = await this.collectionForPath(filepath);
@@ -5782,7 +6333,8 @@ var Database = class {
5782
6333
  );
5783
6334
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
5784
6335
  const collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
5785
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
6336
+ const collectionReferences = (_a = await this.getCollectionReferences()) == null ? void 0 : _a[collection.name];
6337
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
5786
6338
  if (!(collection == null ? void 0 : collection.isDetached)) {
5787
6339
  if (this.bridge) {
5788
6340
  await this.bridge.put(normalizedPath, stringifiedFile);
@@ -5810,6 +6362,14 @@ var Database = class {
5810
6362
  let delOps = [];
5811
6363
  if (!isGitKeep(normalizedPath, collection)) {
5812
6364
  putOps = [
6365
+ ...makeRefOpsForDocument(
6366
+ normalizedPath,
6367
+ collection == null ? void 0 : collection.name,
6368
+ collectionReferences,
6369
+ dataFields,
6370
+ "put",
6371
+ level
6372
+ ),
5813
6373
  ...makeIndexOpsForDocument(
5814
6374
  normalizedPath,
5815
6375
  collection == null ? void 0 : collection.name,
@@ -5818,6 +6378,7 @@ var Database = class {
5818
6378
  "put",
5819
6379
  level
5820
6380
  ),
6381
+ // folder indices
5821
6382
  ...makeIndexOpsForDocument(
5822
6383
  normalizedPath,
5823
6384
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -5832,6 +6393,14 @@ var Database = class {
5832
6393
  SUBLEVEL_OPTIONS
5833
6394
  ).get(normalizedPath);
5834
6395
  delOps = existingItem ? [
6396
+ ...makeRefOpsForDocument(
6397
+ normalizedPath,
6398
+ collection == null ? void 0 : collection.name,
6399
+ collectionReferences,
6400
+ existingItem,
6401
+ "del",
6402
+ level
6403
+ ),
5835
6404
  ...makeIndexOpsForDocument(
5836
6405
  normalizedPath,
5837
6406
  collection == null ? void 0 : collection.name,
@@ -5840,6 +6409,7 @@ var Database = class {
5840
6409
  "del",
5841
6410
  level
5842
6411
  ),
6412
+ // folder indices
5843
6413
  ...makeIndexOpsForDocument(
5844
6414
  normalizedPath,
5845
6415
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -5866,7 +6436,7 @@ var Database = class {
5866
6436
  await level.batch(ops);
5867
6437
  };
5868
6438
  this.put = async (filepath, data, collectionName) => {
5869
- var _a, _b;
6439
+ var _a, _b, _c;
5870
6440
  await this.initLevel();
5871
6441
  try {
5872
6442
  if (SYSTEM_FILES.includes(filepath)) {
@@ -5879,13 +6449,14 @@ var Database = class {
5879
6449
  );
5880
6450
  collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collectionName];
5881
6451
  }
5882
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
6452
+ const collectionReferences = (_a = await this.getCollectionReferences()) == null ? void 0 : _a[collectionName];
6453
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
5883
6454
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5884
6455
  const collection = await this.collectionForPath(filepath);
5885
6456
  if (!collection) {
5886
6457
  throw new import_graphql6.GraphQLError(`Unable to find collection for ${filepath}.`);
5887
6458
  }
5888
- if (((_a = collection.match) == null ? void 0 : _a.exclude) || ((_b = collection.match) == null ? void 0 : _b.include)) {
6459
+ if (((_b = collection.match) == null ? void 0 : _b.exclude) || ((_c = collection.match) == null ? void 0 : _c.include)) {
5889
6460
  const matches = this.tinaSchema.getMatches({ collection });
5890
6461
  const match = import_micromatch2.default.isMatch(filepath, matches);
5891
6462
  if (!match) {
@@ -5926,6 +6497,14 @@ var Database = class {
5926
6497
  let delOps = [];
5927
6498
  if (!isGitKeep(normalizedPath, collection)) {
5928
6499
  putOps = [
6500
+ ...makeRefOpsForDocument(
6501
+ normalizedPath,
6502
+ collectionName,
6503
+ collectionReferences,
6504
+ dataFields,
6505
+ "put",
6506
+ level
6507
+ ),
5929
6508
  ...makeIndexOpsForDocument(
5930
6509
  normalizedPath,
5931
6510
  collectionName,
@@ -5934,6 +6513,7 @@ var Database = class {
5934
6513
  "put",
5935
6514
  level
5936
6515
  ),
6516
+ // folder indices
5937
6517
  ...makeIndexOpsForDocument(
5938
6518
  normalizedPath,
5939
6519
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -5948,6 +6528,14 @@ var Database = class {
5948
6528
  SUBLEVEL_OPTIONS
5949
6529
  ).get(normalizedPath);
5950
6530
  delOps = existingItem ? [
6531
+ ...makeRefOpsForDocument(
6532
+ normalizedPath,
6533
+ collectionName,
6534
+ collectionReferences,
6535
+ existingItem,
6536
+ "del",
6537
+ level
6538
+ ),
5951
6539
  ...makeIndexOpsForDocument(
5952
6540
  normalizedPath,
5953
6541
  collectionName,
@@ -5956,6 +6544,7 @@ var Database = class {
5956
6544
  "del",
5957
6545
  level
5958
6546
  ),
6547
+ // folder indices
5959
6548
  ...makeIndexOpsForDocument(
5960
6549
  normalizedPath,
5961
6550
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -6033,6 +6622,7 @@ var Database = class {
6033
6622
  aliasedData,
6034
6623
  extension,
6035
6624
  writeTemplateKey,
6625
+ //templateInfo.type === 'union',
6036
6626
  {
6037
6627
  frontmatterFormat: collection == null ? void 0 : collection.frontmatterFormat,
6038
6628
  frontmatterDelimiters: collection == null ? void 0 : collection.frontmatterDelimiters
@@ -6050,7 +6640,7 @@ var Database = class {
6050
6640
  };
6051
6641
  this.getLookup = async (returnType) => {
6052
6642
  await this.initLevel();
6053
- const lookupPath = (0, import_schema_tools3.normalizePath)(
6643
+ const lookupPath = (0, import_schema_tools4.normalizePath)(
6054
6644
  import_node_path.default.join(this.getGeneratedFolder(), `_lookup.json`)
6055
6645
  );
6056
6646
  if (!this._lookup) {
@@ -6063,7 +6653,7 @@ var Database = class {
6063
6653
  };
6064
6654
  this.getGraphQLSchema = async () => {
6065
6655
  await this.initLevel();
6066
- const graphqlPath = (0, import_schema_tools3.normalizePath)(
6656
+ const graphqlPath = (0, import_schema_tools4.normalizePath)(
6067
6657
  import_node_path.default.join(this.getGeneratedFolder(), `_graphql.json`)
6068
6658
  );
6069
6659
  return await this.contentLevel.sublevel(
@@ -6071,11 +6661,12 @@ var Database = class {
6071
6661
  SUBLEVEL_OPTIONS
6072
6662
  ).get(graphqlPath);
6073
6663
  };
6664
+ //TODO - is there a reason why the database fetches some config with "bridge.get", and some with "store.get"?
6074
6665
  this.getGraphQLSchemaFromBridge = async () => {
6075
6666
  if (!this.bridge) {
6076
6667
  throw new Error(`No bridge configured`);
6077
6668
  }
6078
- const graphqlPath = (0, import_schema_tools3.normalizePath)(
6669
+ const graphqlPath = (0, import_schema_tools4.normalizePath)(
6079
6670
  import_node_path.default.join(this.getGeneratedFolder(), `_graphql.json`)
6080
6671
  );
6081
6672
  const _graphql = await this.bridge.get(graphqlPath);
@@ -6083,7 +6674,7 @@ var Database = class {
6083
6674
  };
6084
6675
  this.getTinaSchema = async (level) => {
6085
6676
  await this.initLevel();
6086
- const schemaPath = (0, import_schema_tools3.normalizePath)(
6677
+ const schemaPath = (0, import_schema_tools4.normalizePath)(
6087
6678
  import_node_path.default.join(this.getGeneratedFolder(), `_schema.json`)
6088
6679
  );
6089
6680
  return await (level || this.contentLevel).sublevel(
@@ -6099,7 +6690,7 @@ var Database = class {
6099
6690
  const schema = existingSchema || await this.getTinaSchema(level || this.contentLevel);
6100
6691
  if (!schema) {
6101
6692
  throw new Error(
6102
- `Unable to get schema from level db: ${(0, import_schema_tools3.normalizePath)(
6693
+ `Unable to get schema from level db: ${(0, import_schema_tools4.normalizePath)(
6103
6694
  import_node_path.default.join(this.getGeneratedFolder(), `_schema.json`)
6104
6695
  )}`
6105
6696
  );
@@ -6107,6 +6698,22 @@ var Database = class {
6107
6698
  this.tinaSchema = await createSchema({ schema });
6108
6699
  return this.tinaSchema;
6109
6700
  };
6701
+ this.getCollectionReferences = async (level) => {
6702
+ if (this.collectionReferences) {
6703
+ return this.collectionReferences;
6704
+ }
6705
+ const result = {};
6706
+ const schema = await this.getSchema(level || this.contentLevel);
6707
+ const collections = schema.getCollections();
6708
+ for (const collection of collections) {
6709
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6710
+ collection.name
6711
+ );
6712
+ result[collection.name] = collectionReferences;
6713
+ }
6714
+ this.collectionReferences = result;
6715
+ return result;
6716
+ };
6110
6717
  this.getIndexDefinitions = async (level) => {
6111
6718
  if (!this.collectionIndexDefinitions) {
6112
6719
  await new Promise(async (resolve2, reject) => {
@@ -6116,10 +6723,53 @@ var Database = class {
6116
6723
  const collections = schema.getCollections();
6117
6724
  for (const collection of collections) {
6118
6725
  const indexDefinitions = {
6119
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6726
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6727
+ // provide a default sort key which is the file sort
6728
+ // pseudo-index for the collection's references
6729
+ [REFS_COLLECTIONS_SORT_KEY]: {
6730
+ fields: [
6731
+ {
6732
+ name: REFS_REFERENCE_FIELD,
6733
+ type: "string",
6734
+ list: false
6735
+ },
6736
+ {
6737
+ name: REFS_PATH_FIELD,
6738
+ type: "string",
6739
+ list: false
6740
+ }
6741
+ ]
6742
+ }
6120
6743
  };
6121
- if (collection.fields) {
6122
- for (const field of collection.fields) {
6744
+ let fields = [];
6745
+ if (collection.templates) {
6746
+ const templateFieldMap = {};
6747
+ const conflictedFields = /* @__PURE__ */ new Set();
6748
+ for (const template of collection.templates) {
6749
+ for (const field of template.fields) {
6750
+ if (!templateFieldMap[field.name]) {
6751
+ templateFieldMap[field.name] = field;
6752
+ } else {
6753
+ if (templateFieldMap[field.name].type !== field.type) {
6754
+ console.warn(
6755
+ `Field ${field.name} has conflicting types in templates - skipping index`
6756
+ );
6757
+ conflictedFields.add(field.name);
6758
+ }
6759
+ }
6760
+ }
6761
+ }
6762
+ for (const conflictedField in conflictedFields) {
6763
+ delete templateFieldMap[conflictedField];
6764
+ }
6765
+ for (const field of Object.values(templateFieldMap)) {
6766
+ fields.push(field);
6767
+ }
6768
+ } else if (collection.fields) {
6769
+ fields = collection.fields;
6770
+ }
6771
+ if (fields) {
6772
+ for (const field of fields) {
6123
6773
  if (field.indexed !== void 0 && field.indexed === false || field.type === "object") {
6124
6774
  continue;
6125
6775
  }
@@ -6268,29 +6918,36 @@ var Database = class {
6268
6918
  }
6269
6919
  startKey = startKey || key || "";
6270
6920
  endKey = key || "";
6271
- edges = [...edges, { cursor: key, path: filepath }];
6921
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6272
6922
  }
6273
6923
  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
- });
6924
+ edges: await sequential(
6925
+ edges,
6926
+ async ({
6927
+ cursor,
6928
+ path: path7,
6929
+ value
6930
+ }) => {
6931
+ try {
6932
+ const node = await hydrator(path7, value);
6933
+ return {
6934
+ node,
6935
+ cursor: btoa(cursor)
6936
+ };
6937
+ } catch (error) {
6938
+ console.log(error);
6939
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6940
+ throw new TinaQueryError({
6941
+ originalError: error,
6942
+ file: path7,
6943
+ collection: collection.name,
6944
+ stack: error.stack
6945
+ });
6946
+ }
6947
+ throw error;
6290
6948
  }
6291
- throw error;
6292
6949
  }
6293
- }),
6950
+ ),
6294
6951
  pageInfo: {
6295
6952
  hasPreviousPage,
6296
6953
  hasNextPage,
@@ -6315,7 +6972,7 @@ var Database = class {
6315
6972
  try {
6316
6973
  lookup = lookupFromLockFile || JSON.parse(
6317
6974
  await this.bridge.get(
6318
- (0, import_schema_tools3.normalizePath)(
6975
+ (0, import_schema_tools4.normalizePath)(
6319
6976
  import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")
6320
6977
  )
6321
6978
  )
@@ -6340,15 +6997,15 @@ var Database = class {
6340
6997
  }
6341
6998
  const contentRootLevel = nextLevel.sublevel(CONTENT_ROOT_PREFIX, SUBLEVEL_OPTIONS);
6342
6999
  await contentRootLevel.put(
6343
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_graphql.json")),
7000
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_graphql.json")),
6344
7001
  graphQLSchema
6345
7002
  );
6346
7003
  await contentRootLevel.put(
6347
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_schema.json")),
7004
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_schema.json")),
6348
7005
  tinaSchema.schema
6349
7006
  );
6350
7007
  await contentRootLevel.put(
6351
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")),
7008
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")),
6352
7009
  lookup
6353
7010
  );
6354
7011
  const result = await this._indexAllContent(
@@ -6430,23 +7087,25 @@ var Database = class {
6430
7087
  }
6431
7088
  };
6432
7089
  this.delete = async (filepath) => {
7090
+ var _a;
6433
7091
  await this.initLevel();
6434
7092
  const collection = await this.collectionForPath(filepath);
6435
7093
  if (!collection) {
6436
7094
  throw new Error(`No collection found for path: ${filepath}`);
6437
7095
  }
6438
7096
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7097
+ const collectionReferences = (_a = await this.getCollectionReferences()) == null ? void 0 : _a[collection.name];
6439
7098
  const collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
6440
7099
  let level = this.contentLevel;
6441
7100
  if (collection == null ? void 0 : collection.isDetached) {
6442
7101
  level = this.appLevel.sublevel(collection == null ? void 0 : collection.name, SUBLEVEL_OPTIONS);
6443
7102
  }
6444
- const itemKey = (0, import_schema_tools3.normalizePath)(filepath);
7103
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
6445
7104
  const rootSublevel = level.sublevel(
6446
7105
  CONTENT_ROOT_PREFIX,
6447
7106
  SUBLEVEL_OPTIONS
6448
7107
  );
6449
- const item = await rootSublevel.get(itemKey);
7108
+ const item = await rootSublevel.get(normalizedPath);
6450
7109
  if (item) {
6451
7110
  const folderTreeBuilder = new FolderTreeBuilder();
6452
7111
  const folderKey = folderTreeBuilder.update(
@@ -6454,16 +7113,25 @@ var Database = class {
6454
7113
  collection.path || ""
6455
7114
  );
6456
7115
  await this.contentLevel.batch([
7116
+ ...makeRefOpsForDocument(
7117
+ normalizedPath,
7118
+ collection.name,
7119
+ collectionReferences,
7120
+ item,
7121
+ "del",
7122
+ level
7123
+ ),
6457
7124
  ...makeIndexOpsForDocument(
6458
- filepath,
7125
+ normalizedPath,
6459
7126
  collection.name,
6460
7127
  collectionIndexDefinitions,
6461
7128
  item,
6462
7129
  "del",
6463
7130
  level
6464
7131
  ),
7132
+ // folder indices
6465
7133
  ...makeIndexOpsForDocument(
6466
- filepath,
7134
+ normalizedPath,
6467
7135
  `${collection.name}_${folderKey}`,
6468
7136
  collectionIndexDefinitions,
6469
7137
  item,
@@ -6472,17 +7140,17 @@ var Database = class {
6472
7140
  ),
6473
7141
  {
6474
7142
  type: "del",
6475
- key: itemKey,
7143
+ key: normalizedPath,
6476
7144
  sublevel: rootSublevel
6477
7145
  }
6478
7146
  ]);
6479
7147
  }
6480
7148
  if (!(collection == null ? void 0 : collection.isDetached)) {
6481
7149
  if (this.bridge) {
6482
- await this.bridge.delete((0, import_schema_tools3.normalizePath)(filepath));
7150
+ await this.bridge.delete(normalizedPath);
6483
7151
  }
6484
7152
  try {
6485
- await this.onDelete((0, import_schema_tools3.normalizePath)(filepath));
7153
+ await this.onDelete(normalizedPath);
6486
7154
  } catch (e) {
6487
7155
  throw new import_graphql6.GraphQLError(
6488
7156
  `Error running onDelete hook for ${filepath}: ${e}`,
@@ -6530,7 +7198,13 @@ var Database = class {
6530
7198
  );
6531
7199
  }
6532
7200
  } else {
6533
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7201
+ await _indexContent(
7202
+ this,
7203
+ level,
7204
+ contentPaths,
7205
+ enqueueOps,
7206
+ collection
7207
+ );
6534
7208
  }
6535
7209
  }
6536
7210
  );
@@ -6616,6 +7290,9 @@ var Database = class {
6616
7290
  info: templateInfo
6617
7291
  };
6618
7292
  }
7293
+ /**
7294
+ * Clears the internal cache of the tinaSchema and the lookup file. This allows the state to be reset
7295
+ */
6619
7296
  clearCache() {
6620
7297
  this.tinaSchema = null;
6621
7298
  this._lookup = null;
@@ -6667,6 +7344,7 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
6667
7344
  );
6668
7345
  var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${(collection == null ? void 0 : collection.format) || "md"}`);
6669
7346
  var _indexContent = async (database, level, documentPaths, enqueueOps, collection, passwordFields) => {
7347
+ var _a;
6670
7348
  let collectionIndexDefinitions;
6671
7349
  let collectionPath;
6672
7350
  if (collection) {
@@ -6677,6 +7355,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6677
7355
  }
6678
7356
  collectionPath = collection.path;
6679
7357
  }
7358
+ const collectionReferences = (_a = await database.getCollectionReferences()) == null ? void 0 : _a[collection == null ? void 0 : collection.name];
6680
7359
  const tinaSchema = await database.getSchema();
6681
7360
  let templateInfo = null;
6682
7361
  if (collection) {
@@ -6697,13 +7376,60 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6697
7376
  if (passwordFields == null ? void 0 : passwordFields.length) {
6698
7377
  await hashPasswordValues(aliasedData, passwordFields);
6699
7378
  }
6700
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
7379
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
7380
+ const rootSublevel = level.sublevel(
7381
+ CONTENT_ROOT_PREFIX,
7382
+ SUBLEVEL_OPTIONS
7383
+ );
6701
7384
  const folderKey = folderTreeBuilder.update(
6702
7385
  normalizedPath,
6703
7386
  collectionPath || ""
6704
7387
  );
7388
+ const item = await rootSublevel.get(normalizedPath);
7389
+ if (item) {
7390
+ await database.contentLevel.batch([
7391
+ ...makeRefOpsForDocument(
7392
+ normalizedPath,
7393
+ collection == null ? void 0 : collection.name,
7394
+ collectionReferences,
7395
+ item,
7396
+ "del",
7397
+ level
7398
+ ),
7399
+ ...makeIndexOpsForDocument(
7400
+ normalizedPath,
7401
+ collection.name,
7402
+ collectionIndexDefinitions,
7403
+ item,
7404
+ "del",
7405
+ level
7406
+ ),
7407
+ // folder indices
7408
+ ...makeIndexOpsForDocument(
7409
+ normalizedPath,
7410
+ `${collection.name}_${folderKey}`,
7411
+ collectionIndexDefinitions,
7412
+ item,
7413
+ "del",
7414
+ level
7415
+ ),
7416
+ {
7417
+ type: "del",
7418
+ key: normalizedPath,
7419
+ sublevel: rootSublevel
7420
+ }
7421
+ ]);
7422
+ }
6705
7423
  if (!isGitKeep(filepath, collection)) {
6706
7424
  await enqueueOps([
7425
+ ...makeRefOpsForDocument(
7426
+ normalizedPath,
7427
+ collection == null ? void 0 : collection.name,
7428
+ collectionReferences,
7429
+ aliasedData,
7430
+ "put",
7431
+ level
7432
+ ),
6707
7433
  ...makeIndexOpsForDocument(
6708
7434
  normalizedPath,
6709
7435
  collection == null ? void 0 : collection.name,
@@ -6712,6 +7438,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6712
7438
  "put",
6713
7439
  level
6714
7440
  ),
7441
+ // folder indexes
6715
7442
  ...makeIndexOpsForDocument(
6716
7443
  normalizedPath,
6717
7444
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -6753,6 +7480,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6753
7480
  }
6754
7481
  };
6755
7482
  var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection) => {
7483
+ var _a;
6756
7484
  if (!documentPaths.length) {
6757
7485
  return;
6758
7486
  }
@@ -6766,6 +7494,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6766
7494
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6767
7495
  }
6768
7496
  }
7497
+ const collectionReferences = (_a = await database.getCollectionReferences()) == null ? void 0 : _a[collection == null ? void 0 : collection.name];
6769
7498
  const tinaSchema = await database.getSchema();
6770
7499
  let templateInfo = null;
6771
7500
  if (collection) {
@@ -6777,7 +7506,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6777
7506
  );
6778
7507
  const folderTreeBuilder = new FolderTreeBuilder();
6779
7508
  await sequential(documentPaths, async (filepath) => {
6780
- const itemKey = (0, import_schema_tools3.normalizePath)(filepath);
7509
+ const itemKey = (0, import_schema_tools4.normalizePath)(filepath);
6781
7510
  const item = await rootLevel.get(itemKey);
6782
7511
  if (item) {
6783
7512
  const folderKey = folderTreeBuilder.update(
@@ -6789,6 +7518,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6789
7518
  item
6790
7519
  ) : item;
6791
7520
  await enqueueOps([
7521
+ ...makeRefOpsForDocument(
7522
+ itemKey,
7523
+ collection == null ? void 0 : collection.name,
7524
+ collectionReferences,
7525
+ aliasedData,
7526
+ "del",
7527
+ database.contentLevel
7528
+ ),
6792
7529
  ...makeIndexOpsForDocument(
6793
7530
  itemKey,
6794
7531
  collection.name,
@@ -6797,6 +7534,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6797
7534
  "del",
6798
7535
  database.contentLevel
6799
7536
  ),
7537
+ // folder indexes
6800
7538
  ...makeIndexOpsForDocument(
6801
7539
  itemKey,
6802
7540
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -6863,14 +7601,14 @@ var getChangedFiles = async ({
6863
7601
  const rootDir = await findGitRoot(dir);
6864
7602
  let pathPrefix = "";
6865
7603
  if (rootDir !== dir) {
6866
- pathPrefix = (0, import_schema_tools3.normalizePath)(dir.substring(rootDir.length + 1));
7604
+ pathPrefix = (0, import_schema_tools4.normalizePath)(dir.substring(rootDir.length + 1));
6867
7605
  }
6868
7606
  await import_isomorphic_git.default.walk({
6869
7607
  fs: fs4,
6870
7608
  dir: rootDir,
6871
7609
  trees: [import_isomorphic_git.default.TREE({ ref: from }), import_isomorphic_git.default.TREE({ ref: to })],
6872
7610
  map: async function(filename, [A, B]) {
6873
- const relativePath = (0, import_schema_tools3.normalizePath)(filename).substring(pathPrefix.length);
7611
+ const relativePath = (0, import_schema_tools4.normalizePath)(filename).substring(pathPrefix.length);
6874
7612
  let matches = false;
6875
7613
  for (const [key, matcher] of Object.entries(pathFilter)) {
6876
7614
  if (relativePath.startsWith(key)) {
@@ -7012,17 +7750,26 @@ var IsomorphicBridge = class {
7012
7750
  getAuthor() {
7013
7751
  return {
7014
7752
  ...this.author,
7015
- timestamp: Math.round(new Date().getTime() / 1e3),
7753
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
7016
7754
  timezoneOffset: 0
7017
7755
  };
7018
7756
  }
7019
7757
  getCommitter() {
7020
7758
  return {
7021
7759
  ...this.committer,
7022
- timestamp: Math.round(new Date().getTime() / 1e3),
7760
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
7023
7761
  timezoneOffset: 0
7024
7762
  };
7025
7763
  }
7764
+ /**
7765
+ * Recursively populate paths matching `pattern` for the given `entry`
7766
+ *
7767
+ * @param pattern - pattern to filter paths by
7768
+ * @param entry - TreeEntry to start building list from
7769
+ * @param path - base path
7770
+ * @param results
7771
+ * @private
7772
+ */
7026
7773
  async listEntries({
7027
7774
  pattern,
7028
7775
  entry,
@@ -7055,6 +7802,15 @@ var IsomorphicBridge = class {
7055
7802
  });
7056
7803
  }
7057
7804
  }
7805
+ /**
7806
+ * For the specified path, returns an object with an array containing the parts of the path (pathParts)
7807
+ * and an array containing the WalkerEntry objects for the path parts (pathEntries). Any null elements in the
7808
+ * pathEntries are placeholders for non-existent entries.
7809
+ *
7810
+ * @param path - path being resolved
7811
+ * @param ref - ref to resolve path entries for
7812
+ * @private
7813
+ */
7058
7814
  async resolvePathEntries(path7, ref) {
7059
7815
  let pathParts = path7.split("/");
7060
7816
  const result = await import_isomorphic_git2.default.walk({
@@ -7085,6 +7841,17 @@ var IsomorphicBridge = class {
7085
7841
  }
7086
7842
  return { pathParts, pathEntries };
7087
7843
  }
7844
+ /**
7845
+ * Updates tree entry and associated parent tree entries
7846
+ *
7847
+ * @param existingOid - the existing OID
7848
+ * @param updatedOid - the updated OID
7849
+ * @param path - the path of the entry being updated
7850
+ * @param type - the type of the entry being updated (blob or tree)
7851
+ * @param pathEntries - parent path entries
7852
+ * @param pathParts - parent path parts
7853
+ * @private
7854
+ */
7088
7855
  async updateTreeHierarchy(existingOid, updatedOid, path7, type, pathEntries, pathParts) {
7089
7856
  const lastIdx = pathEntries.length - 1;
7090
7857
  const parentEntry = pathEntries[lastIdx];
@@ -7140,6 +7907,13 @@ var IsomorphicBridge = class {
7140
7907
  );
7141
7908
  }
7142
7909
  }
7910
+ /**
7911
+ * Creates a commit for the specified tree and updates the specified ref to point to the commit
7912
+ *
7913
+ * @param treeSha - sha of the new tree
7914
+ * @param ref - the ref that should be updated
7915
+ * @private
7916
+ */
7143
7917
  async commitTree(treeSha, ref) {
7144
7918
  const commitSha = await import_isomorphic_git2.default.writeCommit({
7145
7919
  ...this.isomorphicConfig,
@@ -7152,6 +7926,7 @@ var IsomorphicBridge = class {
7152
7926
  })
7153
7927
  ],
7154
7928
  message: this.commitMessage,
7929
+ // TODO these should be configurable
7155
7930
  author: this.getAuthor(),
7156
7931
  committer: this.getCommitter()
7157
7932
  }
@@ -7390,5 +8165,5 @@ var buildSchema = async (config, flags) => {
7390
8165
  transformDocument,
7391
8166
  transformDocumentIntoPayload
7392
8167
  });
7393
- //! Replaces _.flattenDeep()
7394
8168
  //! Replaces _.get()
8169
+ //! Replaces _.flattenDeep()