@tinacms/graphql 0.0.0-c8b1d84-20241003015733 → 0.0.0-cacbd0e-20250306051805

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 Tina Cloud 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.14",
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,30 @@ 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)({ path: parentPath, json: obj, resultType: "value" });
4902
+ if (parents.length > 0) {
4903
+ parents.forEach((parent) => {
4904
+ if (parent && typeof parent === "object" && keyToUpdate in parent) {
4905
+ if (parent[keyToUpdate] === oldValue) {
4906
+ parent[keyToUpdate] = newValue;
4907
+ updated = true;
4908
+ }
4909
+ }
4910
+ });
4911
+ }
4912
+ return { object: obj, updated };
4913
+ };
4617
4914
  var Resolver = class {
4618
4915
  constructor(init) {
4619
4916
  this.init = init;
@@ -4621,6 +4918,7 @@ var Resolver = class {
4621
4918
  const collection = this.tinaSchema.getCollection(collectionName);
4622
4919
  const extraFields = {};
4623
4920
  return {
4921
+ // return the collection and hasDocuments to resolve documents at a lower level
4624
4922
  documents: { collection, hasDocuments },
4625
4923
  ...collection,
4626
4924
  ...extraFields
@@ -4628,7 +4926,9 @@ var Resolver = class {
4628
4926
  };
4629
4927
  this.getRaw = async (fullPath) => {
4630
4928
  if (typeof fullPath !== "string") {
4631
- throw new Error(`fullPath must be of type string for getDocument request`);
4929
+ throw new Error(
4930
+ `fullPath must be of type string for getDocument request`
4931
+ );
4632
4932
  }
4633
4933
  return this.database.get(fullPath);
4634
4934
  };
@@ -4655,22 +4955,28 @@ var Resolver = class {
4655
4955
  );
4656
4956
  }
4657
4957
  };
4658
- this.getDocument = async (fullPath) => {
4958
+ this.getDocument = async (fullPath, opts = {}) => {
4659
4959
  if (typeof fullPath !== "string") {
4660
- throw new Error(`fullPath must be of type string for getDocument request`);
4960
+ throw new Error(
4961
+ `fullPath must be of type string for getDocument request`
4962
+ );
4661
4963
  }
4662
4964
  const rawData = await this.getRaw(fullPath);
4965
+ const hasReferences = (opts == null ? void 0 : opts.checkReferences) ? await this.hasReferences(fullPath, opts.collection) : void 0;
4663
4966
  return transformDocumentIntoPayload(
4664
4967
  fullPath,
4665
4968
  rawData,
4666
4969
  this.tinaSchema,
4667
4970
  this.config,
4668
- this.isAudit
4971
+ this.isAudit,
4972
+ hasReferences
4669
4973
  );
4670
4974
  };
4671
4975
  this.deleteDocument = async (fullPath) => {
4672
4976
  if (typeof fullPath !== "string") {
4673
- throw new Error(`fullPath must be of type string for getDocument request`);
4977
+ throw new Error(
4978
+ `fullPath must be of type string for getDocument request`
4979
+ );
4674
4980
  }
4675
4981
  await this.database.delete(fullPath);
4676
4982
  };
@@ -4705,7 +5011,9 @@ var Resolver = class {
4705
5011
  );
4706
5012
  } else {
4707
5013
  return this.buildFieldMutations(
5014
+ // @ts-ignore FIXME Argument of type 'string | object' is not assignable to parameter of type '{ [fieldName: string]: string | object | (string | object)[]; }'
4708
5015
  fieldValue,
5016
+ //@ts-ignore
4709
5017
  objectTemplate,
4710
5018
  existingData
4711
5019
  );
@@ -4717,6 +5025,7 @@ var Resolver = class {
4717
5025
  fieldValue.map(async (item) => {
4718
5026
  if (typeof item === "string") {
4719
5027
  throw new Error(
5028
+ //@ts-ignore
4720
5029
  `Expected object for template value for field ${field.name}`
4721
5030
  );
4722
5031
  }
@@ -4725,16 +5034,19 @@ var Resolver = class {
4725
5034
  });
4726
5035
  const [templateName] = Object.entries(item)[0];
4727
5036
  const template = templates.find(
5037
+ //@ts-ignore
4728
5038
  (template2) => template2.name === templateName
4729
5039
  );
4730
5040
  if (!template) {
4731
5041
  throw new Error(`Expected to find template ${templateName}`);
4732
5042
  }
4733
5043
  return {
5044
+ // @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
5045
  ...await this.buildFieldMutations(
4735
5046
  item[template.name],
4736
5047
  template
4737
5048
  ),
5049
+ //@ts-ignore
4738
5050
  _template: template.name
4739
5051
  };
4740
5052
  })
@@ -4742,6 +5054,7 @@ var Resolver = class {
4742
5054
  } else {
4743
5055
  if (typeof fieldValue === "string") {
4744
5056
  throw new Error(
5057
+ //@ts-ignore
4745
5058
  `Expected object for template value for field ${field.name}`
4746
5059
  );
4747
5060
  }
@@ -4750,16 +5063,19 @@ var Resolver = class {
4750
5063
  });
4751
5064
  const [templateName] = Object.entries(fieldValue)[0];
4752
5065
  const template = templates.find(
5066
+ //@ts-ignore
4753
5067
  (template2) => template2.name === templateName
4754
5068
  );
4755
5069
  if (!template) {
4756
5070
  throw new Error(`Expected to find template ${templateName}`);
4757
5071
  }
4758
5072
  return {
5073
+ // @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
5074
  ...await this.buildFieldMutations(
4760
5075
  fieldValue[template.name],
4761
5076
  template
4762
5077
  ),
5078
+ //@ts-ignore
4763
5079
  _template: template.name
4764
5080
  };
4765
5081
  }
@@ -4799,6 +5115,7 @@ var Resolver = class {
4799
5115
  return this.getDocument(realPath);
4800
5116
  }
4801
5117
  const params = await this.buildObjectMutations(
5118
+ // @ts-ignore
4802
5119
  args.params[collection.name],
4803
5120
  collection
4804
5121
  );
@@ -4844,6 +5161,7 @@ var Resolver = class {
4844
5161
  const values = {
4845
5162
  ...oldDoc,
4846
5163
  ...await this.buildFieldMutations(
5164
+ // @ts-ignore FIXME: failing on unknown, which we don't need to know because it's recursive
4847
5165
  templateParams,
4848
5166
  template,
4849
5167
  doc == null ? void 0 : doc._rawData
@@ -4857,13 +5175,22 @@ var Resolver = class {
4857
5175
  return this.getDocument(realPath);
4858
5176
  }
4859
5177
  const params = await this.buildObjectMutations(
5178
+ //@ts-ignore
4860
5179
  isCollectionSpecific ? args.params : args.params[collection.name],
4861
5180
  collection,
4862
5181
  doc == null ? void 0 : doc._rawData
4863
5182
  );
4864
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
5183
+ await this.database.put(
5184
+ realPath,
5185
+ { ...oldDoc, ...params },
5186
+ collection.name
5187
+ );
4865
5188
  return this.getDocument(realPath);
4866
5189
  };
5190
+ /**
5191
+ * Returns top-level fields which are not defined in the collection, so their
5192
+ * values are not eliminated from Tina when new values are saved
5193
+ */
4867
5194
  this.resolveLegacyValues = (oldDoc, collection) => {
4868
5195
  const legacyValues = {};
4869
5196
  Object.entries(oldDoc).forEach(([key, value]) => {
@@ -4969,6 +5296,40 @@ var Resolver = class {
4969
5296
  if (isDeletion) {
4970
5297
  const doc = await this.getDocument(realPath);
4971
5298
  await this.deleteDocument(realPath);
5299
+ if (await this.hasReferences(realPath, collection)) {
5300
+ const collRefs = await this.findReferences(realPath, collection);
5301
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5302
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5303
+ docsWithRefs
5304
+ )) {
5305
+ let refDoc = await this.getRaw(pathToDocWithRef);
5306
+ let hasUpdate = false;
5307
+ for (const path7 of referencePaths) {
5308
+ const { object: object2, updated } = updateObjectWithJsonPath(
5309
+ refDoc,
5310
+ path7,
5311
+ realPath,
5312
+ null
5313
+ );
5314
+ refDoc = object2;
5315
+ hasUpdate = updated || hasUpdate;
5316
+ }
5317
+ if (hasUpdate) {
5318
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5319
+ if (!collectionWithRef) {
5320
+ throw new Error(
5321
+ `Unable to find collection for ${pathToDocWithRef}`
5322
+ );
5323
+ }
5324
+ await this.database.put(
5325
+ pathToDocWithRef,
5326
+ refDoc,
5327
+ collectionWithRef.name
5328
+ );
5329
+ }
5330
+ }
5331
+ }
5332
+ }
4972
5333
  return doc;
4973
5334
  }
4974
5335
  if (isUpdateName) {
@@ -4985,12 +5346,49 @@ var Resolver = class {
4985
5346
  collection == null ? void 0 : collection.path,
4986
5347
  args.params.relativePath
4987
5348
  );
5349
+ if (newRealPath === realPath) {
5350
+ return doc;
5351
+ }
4988
5352
  await this.database.put(newRealPath, doc._rawData, collection.name);
4989
5353
  await this.deleteDocument(realPath);
5354
+ const collRefs = await this.findReferences(realPath, collection);
5355
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5356
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5357
+ docsWithRefs
5358
+ )) {
5359
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5360
+ let hasUpdate = false;
5361
+ for (const path7 of referencePaths) {
5362
+ const { object: object2, updated } = updateObjectWithJsonPath(
5363
+ docWithRef,
5364
+ path7,
5365
+ realPath,
5366
+ newRealPath
5367
+ );
5368
+ docWithRef = object2;
5369
+ hasUpdate = updated || hasUpdate;
5370
+ }
5371
+ if (hasUpdate) {
5372
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5373
+ if (!collectionWithRef) {
5374
+ throw new Error(
5375
+ `Unable to find collection for ${pathToDocWithRef}`
5376
+ );
5377
+ }
5378
+ await this.database.put(
5379
+ pathToDocWithRef,
5380
+ docWithRef,
5381
+ collectionWithRef.name
5382
+ );
5383
+ }
5384
+ }
5385
+ }
4990
5386
  return this.getDocument(newRealPath);
4991
5387
  }
4992
5388
  if (alreadyExists === false) {
4993
- throw new Error(`Unable to update document, ${realPath} does not exist`);
5389
+ throw new Error(
5390
+ `Unable to update document, ${realPath} does not exist`
5391
+ );
4994
5392
  }
4995
5393
  return this.updateResolveDocument({
4996
5394
  collection,
@@ -5000,7 +5398,10 @@ var Resolver = class {
5000
5398
  isCollectionSpecific
5001
5399
  });
5002
5400
  } else {
5003
- return this.getDocument(realPath);
5401
+ return this.getDocument(realPath, {
5402
+ collection,
5403
+ checkReferences: true
5404
+ });
5004
5405
  }
5005
5406
  };
5006
5407
  this.resolveCollectionConnections = async ({ ids }) => {
@@ -5037,6 +5438,7 @@ var Resolver = class {
5037
5438
  },
5038
5439
  collection: referencedCollection,
5039
5440
  hydrator: (path7) => path7
5441
+ // just return the path
5040
5442
  }
5041
5443
  );
5042
5444
  const { edges } = resolvedCollectionConnection;
@@ -5104,6 +5506,82 @@ var Resolver = class {
5104
5506
  }
5105
5507
  };
5106
5508
  };
5509
+ /**
5510
+ * Checks if a document has references to it
5511
+ * @param id The id of the document to check for references
5512
+ * @param c The collection to check for references
5513
+ * @returns true if the document has references, false otherwise
5514
+ */
5515
+ this.hasReferences = async (id, c) => {
5516
+ let count = 0;
5517
+ await this.database.query(
5518
+ {
5519
+ collection: c.name,
5520
+ filterChain: makeFilterChain({
5521
+ conditions: [
5522
+ {
5523
+ filterPath: REFS_REFERENCE_FIELD,
5524
+ filterExpression: {
5525
+ _type: "string",
5526
+ _list: false,
5527
+ eq: id
5528
+ }
5529
+ }
5530
+ ]
5531
+ }),
5532
+ sort: REFS_COLLECTIONS_SORT_KEY
5533
+ },
5534
+ (refId) => {
5535
+ count++;
5536
+ return refId;
5537
+ }
5538
+ );
5539
+ if (count) {
5540
+ return true;
5541
+ }
5542
+ return false;
5543
+ };
5544
+ /**
5545
+ * Finds references to a document
5546
+ * @param id the id of the document to find references to
5547
+ * @param c the collection to find references in
5548
+ * @returns a map of references to the document
5549
+ */
5550
+ this.findReferences = async (id, c) => {
5551
+ const references = {};
5552
+ await this.database.query(
5553
+ {
5554
+ collection: c.name,
5555
+ filterChain: makeFilterChain({
5556
+ conditions: [
5557
+ {
5558
+ filterPath: REFS_REFERENCE_FIELD,
5559
+ filterExpression: {
5560
+ _type: "string",
5561
+ _list: false,
5562
+ eq: id
5563
+ }
5564
+ }
5565
+ ]
5566
+ }),
5567
+ sort: REFS_COLLECTIONS_SORT_KEY
5568
+ },
5569
+ (refId, rawItem) => {
5570
+ if (!references[c.name]) {
5571
+ references[c.name] = {};
5572
+ }
5573
+ if (!references[c.name][refId]) {
5574
+ references[c.name][refId] = [];
5575
+ }
5576
+ const referencePath = rawItem == null ? void 0 : rawItem[REFS_PATH_FIELD];
5577
+ if (referencePath) {
5578
+ references[c.name][refId].push(referencePath);
5579
+ }
5580
+ return refId;
5581
+ }
5582
+ );
5583
+ return references;
5584
+ };
5107
5585
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
5108
5586
  var _a;
5109
5587
  const accum = {};
@@ -5191,6 +5669,27 @@ var Resolver = class {
5191
5669
  }
5192
5670
  return accum;
5193
5671
  };
5672
+ /**
5673
+ * A mutation looks nearly identical between updateDocument:
5674
+ * ```graphql
5675
+ * updateDocument(collection: $collection,relativePath: $path, params: {
5676
+ * post: {
5677
+ * title: "Hello, World"
5678
+ * }
5679
+ * })`
5680
+ * ```
5681
+ * and `updatePostDocument`:
5682
+ * ```graphql
5683
+ * updatePostDocument(relativePath: $path, params: {
5684
+ * title: "Hello, World"
5685
+ * })
5686
+ * ```
5687
+ * The problem here is that we don't know whether the payload came from `updateDocument`
5688
+ * or `updatePostDocument` (we could, but for now it's easier not to pipe those details through),
5689
+ * But we do know that when given a `args.collection` value, we can assume that
5690
+ * this was a `updateDocument` request, and thus - should grab the data
5691
+ * from the corresponding field name in the key
5692
+ */
5194
5693
  this.buildParams = (args) => {
5195
5694
  try {
5196
5695
  assertShape(
@@ -5291,7 +5790,10 @@ var resolve = async ({
5291
5790
  const graphQLSchema = (0, import_graphql5.buildASTSchema)(graphQLSchemaAst);
5292
5791
  const tinaConfig = await database.getTinaSchema();
5293
5792
  const tinaSchema = await createSchema({
5793
+ // TODO: please update all the types to import from @tinacms/schema-tools
5794
+ // @ts-ignore
5294
5795
  schema: tinaConfig,
5796
+ // @ts-ignore
5295
5797
  flags: (_a = tinaConfig == null ? void 0 : tinaConfig.meta) == null ? void 0 : _a.flags
5296
5798
  });
5297
5799
  const resolver = createResolver({
@@ -5308,8 +5810,7 @@ var resolve = async ({
5308
5810
  database
5309
5811
  },
5310
5812
  typeResolver: async (source, _args, info) => {
5311
- if (source.__typename)
5312
- return source.__typename;
5813
+ if (source.__typename) return source.__typename;
5313
5814
  const namedType = (0, import_graphql5.getNamedType)(info.returnType).toString();
5314
5815
  const lookup = await database.getLookup(namedType);
5315
5816
  if (lookup.resolveType === "unionData") {
@@ -5461,11 +5962,13 @@ var resolve = async ({
5461
5962
  (0, import_lodash4.default)(
5462
5963
  params,
5463
5964
  userField.path.slice(1),
5965
+ // remove _rawData from users path
5464
5966
  users.map((u) => {
5465
5967
  if (user[idFieldName] === u[idFieldName]) {
5466
5968
  return user;
5467
5969
  }
5468
5970
  return {
5971
+ // don't overwrite other users' passwords
5469
5972
  ...u,
5470
5973
  [passwordFieldName]: {
5471
5974
  ...u[passwordFieldName],
@@ -5488,6 +5991,9 @@ var resolve = async ({
5488
5991
  }
5489
5992
  const isCreation = lookup[info.fieldName] === "create";
5490
5993
  switch (lookup.resolveType) {
5994
+ /**
5995
+ * `node(id: $id)`
5996
+ */
5491
5997
  case "nodeDocument":
5492
5998
  assertShape(
5493
5999
  args,
@@ -5519,6 +6025,7 @@ var resolve = async ({
5519
6025
  collection: args.collection,
5520
6026
  isMutation,
5521
6027
  isCreation,
6028
+ // Right now this is the only case for deletion
5522
6029
  isDeletion: info.fieldName === "deleteDocument",
5523
6030
  isFolderCreation: info.fieldName === "createFolder",
5524
6031
  isUpdateName: Boolean((_a2 = args == null ? void 0 : args.params) == null ? void 0 : _a2.relativePath),
@@ -5528,6 +6035,9 @@ var resolve = async ({
5528
6035
  return result;
5529
6036
  }
5530
6037
  return value;
6038
+ /**
6039
+ * eg `getMovieDocument.data.actors`
6040
+ */
5531
6041
  case "multiCollectionDocumentList":
5532
6042
  if (Array.isArray(value)) {
5533
6043
  return {
@@ -5539,7 +6049,15 @@ var resolve = async ({
5539
6049
  }
5540
6050
  if (info.fieldName === "documents" && (value == null ? void 0 : value.collection) && (value == null ? void 0 : value.hasDocuments)) {
5541
6051
  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") {
6052
+ if (
6053
+ // 1. Make sure that the filter exists
6054
+ typeof (args == null ? void 0 : args.filter) !== "undefined" && (args == null ? void 0 : args.filter) !== null && // 2. Make sure that the collection name exists
6055
+ // @ts-ignore
6056
+ 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
6057
+ // @ts-ignore
6058
+ Object.keys(args.filter).includes((_c = value == null ? void 0 : value.collection) == null ? void 0 : _c.name) && // @ts-ignore
6059
+ typeof args.filter[(_d = value == null ? void 0 : value.collection) == null ? void 0 : _d.name] !== "undefined"
6060
+ ) {
5543
6061
  filter = args.filter[value.collection.name];
5544
6062
  }
5545
6063
  return resolver.resolveCollectionConnection({
@@ -5547,12 +6065,20 @@ var resolve = async ({
5547
6065
  ...args,
5548
6066
  filter
5549
6067
  },
6068
+ // @ts-ignore
5550
6069
  collection: value.collection
5551
6070
  });
5552
6071
  }
5553
6072
  throw new Error(
5554
6073
  `Expected an array for result of ${info.fieldName} at ${info.path}`
5555
6074
  );
6075
+ /**
6076
+ * Collections-specific getter
6077
+ * eg. `getPostDocument`/`createPostDocument`/`updatePostDocument`
6078
+ *
6079
+ * if coming from a query result
6080
+ * the field will be `node`
6081
+ */
5556
6082
  case "collectionDocument": {
5557
6083
  if (value) {
5558
6084
  return value;
@@ -5567,11 +6093,32 @@ var resolve = async ({
5567
6093
  });
5568
6094
  return result;
5569
6095
  }
6096
+ /**
6097
+ * Collections-specific list getter
6098
+ * eg. `getPageList`
6099
+ */
5570
6100
  case "collectionDocumentList":
5571
6101
  return resolver.resolveCollectionConnection({
5572
6102
  args,
5573
6103
  collection: tinaSchema.getCollection(lookup.collection)
5574
6104
  });
6105
+ /**
6106
+ * A polymorphic data set, it can be from a document's data
6107
+ * of any nested object which can be one of many shapes
6108
+ *
6109
+ * ```graphql
6110
+ * getPostDocument(relativePath: $relativePath) {
6111
+ * data {...} <- this part
6112
+ * }
6113
+ * ```
6114
+ * ```graphql
6115
+ * getBlockDocument(relativePath: $relativePath) {
6116
+ * data {
6117
+ * blocks {...} <- or this part
6118
+ * }
6119
+ * }
6120
+ * ```
6121
+ */
5575
6122
  case "unionData":
5576
6123
  if (!value) {
5577
6124
  if (args.relativePath) {
@@ -5636,8 +6183,7 @@ var TinaLevelClient = class extends import_many_level.ManyLevelGuest {
5636
6183
  this.port = port || 9e3;
5637
6184
  }
5638
6185
  openConnection() {
5639
- if (this._connected)
5640
- return;
6186
+ if (this._connected) return;
5641
6187
  const socket = (0, import_net.connect)(this.port);
5642
6188
  (0, import_readable_stream.pipeline)(socket, this.createRpcStream(), socket, () => {
5643
6189
  this._connected = false;
@@ -5757,7 +6303,7 @@ var Database = class {
5757
6303
  const contentObject = await level.sublevel(
5758
6304
  CONTENT_ROOT_PREFIX,
5759
6305
  SUBLEVEL_OPTIONS
5760
- ).get((0, import_schema_tools3.normalizePath)(filepath));
6306
+ ).get((0, import_schema_tools4.normalizePath)(filepath));
5761
6307
  if (!contentObject) {
5762
6308
  throw new NotFoundError(`Unable to find record ${filepath}`);
5763
6309
  }
@@ -5769,6 +6315,7 @@ var Database = class {
5769
6315
  }
5770
6316
  };
5771
6317
  this.addPendingDocument = async (filepath, data) => {
6318
+ var _a;
5772
6319
  await this.initLevel();
5773
6320
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5774
6321
  const collection = await this.collectionForPath(filepath);
@@ -5782,7 +6329,8 @@ var Database = class {
5782
6329
  );
5783
6330
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
5784
6331
  const collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
5785
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
6332
+ const collectionReferences = (_a = await this.getCollectionReferences()) == null ? void 0 : _a[collection.name];
6333
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
5786
6334
  if (!(collection == null ? void 0 : collection.isDetached)) {
5787
6335
  if (this.bridge) {
5788
6336
  await this.bridge.put(normalizedPath, stringifiedFile);
@@ -5810,6 +6358,14 @@ var Database = class {
5810
6358
  let delOps = [];
5811
6359
  if (!isGitKeep(normalizedPath, collection)) {
5812
6360
  putOps = [
6361
+ ...makeRefOpsForDocument(
6362
+ normalizedPath,
6363
+ collection == null ? void 0 : collection.name,
6364
+ collectionReferences,
6365
+ dataFields,
6366
+ "put",
6367
+ level
6368
+ ),
5813
6369
  ...makeIndexOpsForDocument(
5814
6370
  normalizedPath,
5815
6371
  collection == null ? void 0 : collection.name,
@@ -5818,6 +6374,7 @@ var Database = class {
5818
6374
  "put",
5819
6375
  level
5820
6376
  ),
6377
+ // folder indices
5821
6378
  ...makeIndexOpsForDocument(
5822
6379
  normalizedPath,
5823
6380
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -5832,6 +6389,14 @@ var Database = class {
5832
6389
  SUBLEVEL_OPTIONS
5833
6390
  ).get(normalizedPath);
5834
6391
  delOps = existingItem ? [
6392
+ ...makeRefOpsForDocument(
6393
+ normalizedPath,
6394
+ collection == null ? void 0 : collection.name,
6395
+ collectionReferences,
6396
+ existingItem,
6397
+ "del",
6398
+ level
6399
+ ),
5835
6400
  ...makeIndexOpsForDocument(
5836
6401
  normalizedPath,
5837
6402
  collection == null ? void 0 : collection.name,
@@ -5840,6 +6405,7 @@ var Database = class {
5840
6405
  "del",
5841
6406
  level
5842
6407
  ),
6408
+ // folder indices
5843
6409
  ...makeIndexOpsForDocument(
5844
6410
  normalizedPath,
5845
6411
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -5866,7 +6432,7 @@ var Database = class {
5866
6432
  await level.batch(ops);
5867
6433
  };
5868
6434
  this.put = async (filepath, data, collectionName) => {
5869
- var _a, _b;
6435
+ var _a, _b, _c;
5870
6436
  await this.initLevel();
5871
6437
  try {
5872
6438
  if (SYSTEM_FILES.includes(filepath)) {
@@ -5879,13 +6445,14 @@ var Database = class {
5879
6445
  );
5880
6446
  collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collectionName];
5881
6447
  }
5882
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
6448
+ const collectionReferences = (_a = await this.getCollectionReferences()) == null ? void 0 : _a[collectionName];
6449
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
5883
6450
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5884
6451
  const collection = await this.collectionForPath(filepath);
5885
6452
  if (!collection) {
5886
6453
  throw new import_graphql6.GraphQLError(`Unable to find collection for ${filepath}.`);
5887
6454
  }
5888
- if (((_a = collection.match) == null ? void 0 : _a.exclude) || ((_b = collection.match) == null ? void 0 : _b.include)) {
6455
+ if (((_b = collection.match) == null ? void 0 : _b.exclude) || ((_c = collection.match) == null ? void 0 : _c.include)) {
5889
6456
  const matches = this.tinaSchema.getMatches({ collection });
5890
6457
  const match = import_micromatch2.default.isMatch(filepath, matches);
5891
6458
  if (!match) {
@@ -5926,6 +6493,14 @@ var Database = class {
5926
6493
  let delOps = [];
5927
6494
  if (!isGitKeep(normalizedPath, collection)) {
5928
6495
  putOps = [
6496
+ ...makeRefOpsForDocument(
6497
+ normalizedPath,
6498
+ collectionName,
6499
+ collectionReferences,
6500
+ dataFields,
6501
+ "put",
6502
+ level
6503
+ ),
5929
6504
  ...makeIndexOpsForDocument(
5930
6505
  normalizedPath,
5931
6506
  collectionName,
@@ -5934,6 +6509,7 @@ var Database = class {
5934
6509
  "put",
5935
6510
  level
5936
6511
  ),
6512
+ // folder indices
5937
6513
  ...makeIndexOpsForDocument(
5938
6514
  normalizedPath,
5939
6515
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -5948,6 +6524,14 @@ var Database = class {
5948
6524
  SUBLEVEL_OPTIONS
5949
6525
  ).get(normalizedPath);
5950
6526
  delOps = existingItem ? [
6527
+ ...makeRefOpsForDocument(
6528
+ normalizedPath,
6529
+ collectionName,
6530
+ collectionReferences,
6531
+ existingItem,
6532
+ "del",
6533
+ level
6534
+ ),
5951
6535
  ...makeIndexOpsForDocument(
5952
6536
  normalizedPath,
5953
6537
  collectionName,
@@ -5956,6 +6540,7 @@ var Database = class {
5956
6540
  "del",
5957
6541
  level
5958
6542
  ),
6543
+ // folder indices
5959
6544
  ...makeIndexOpsForDocument(
5960
6545
  normalizedPath,
5961
6546
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -6033,6 +6618,7 @@ var Database = class {
6033
6618
  aliasedData,
6034
6619
  extension,
6035
6620
  writeTemplateKey,
6621
+ //templateInfo.type === 'union',
6036
6622
  {
6037
6623
  frontmatterFormat: collection == null ? void 0 : collection.frontmatterFormat,
6038
6624
  frontmatterDelimiters: collection == null ? void 0 : collection.frontmatterDelimiters
@@ -6050,7 +6636,7 @@ var Database = class {
6050
6636
  };
6051
6637
  this.getLookup = async (returnType) => {
6052
6638
  await this.initLevel();
6053
- const lookupPath = (0, import_schema_tools3.normalizePath)(
6639
+ const lookupPath = (0, import_schema_tools4.normalizePath)(
6054
6640
  import_node_path.default.join(this.getGeneratedFolder(), `_lookup.json`)
6055
6641
  );
6056
6642
  if (!this._lookup) {
@@ -6063,7 +6649,7 @@ var Database = class {
6063
6649
  };
6064
6650
  this.getGraphQLSchema = async () => {
6065
6651
  await this.initLevel();
6066
- const graphqlPath = (0, import_schema_tools3.normalizePath)(
6652
+ const graphqlPath = (0, import_schema_tools4.normalizePath)(
6067
6653
  import_node_path.default.join(this.getGeneratedFolder(), `_graphql.json`)
6068
6654
  );
6069
6655
  return await this.contentLevel.sublevel(
@@ -6071,11 +6657,12 @@ var Database = class {
6071
6657
  SUBLEVEL_OPTIONS
6072
6658
  ).get(graphqlPath);
6073
6659
  };
6660
+ //TODO - is there a reason why the database fetches some config with "bridge.get", and some with "store.get"?
6074
6661
  this.getGraphQLSchemaFromBridge = async () => {
6075
6662
  if (!this.bridge) {
6076
6663
  throw new Error(`No bridge configured`);
6077
6664
  }
6078
- const graphqlPath = (0, import_schema_tools3.normalizePath)(
6665
+ const graphqlPath = (0, import_schema_tools4.normalizePath)(
6079
6666
  import_node_path.default.join(this.getGeneratedFolder(), `_graphql.json`)
6080
6667
  );
6081
6668
  const _graphql = await this.bridge.get(graphqlPath);
@@ -6083,7 +6670,7 @@ var Database = class {
6083
6670
  };
6084
6671
  this.getTinaSchema = async (level) => {
6085
6672
  await this.initLevel();
6086
- const schemaPath = (0, import_schema_tools3.normalizePath)(
6673
+ const schemaPath = (0, import_schema_tools4.normalizePath)(
6087
6674
  import_node_path.default.join(this.getGeneratedFolder(), `_schema.json`)
6088
6675
  );
6089
6676
  return await (level || this.contentLevel).sublevel(
@@ -6099,7 +6686,7 @@ var Database = class {
6099
6686
  const schema = existingSchema || await this.getTinaSchema(level || this.contentLevel);
6100
6687
  if (!schema) {
6101
6688
  throw new Error(
6102
- `Unable to get schema from level db: ${(0, import_schema_tools3.normalizePath)(
6689
+ `Unable to get schema from level db: ${(0, import_schema_tools4.normalizePath)(
6103
6690
  import_node_path.default.join(this.getGeneratedFolder(), `_schema.json`)
6104
6691
  )}`
6105
6692
  );
@@ -6107,6 +6694,22 @@ var Database = class {
6107
6694
  this.tinaSchema = await createSchema({ schema });
6108
6695
  return this.tinaSchema;
6109
6696
  };
6697
+ this.getCollectionReferences = async (level) => {
6698
+ if (this.collectionReferences) {
6699
+ return this.collectionReferences;
6700
+ }
6701
+ const result = {};
6702
+ const schema = await this.getSchema(level || this.contentLevel);
6703
+ const collections = schema.getCollections();
6704
+ for (const collection of collections) {
6705
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6706
+ collection.name
6707
+ );
6708
+ result[collection.name] = collectionReferences;
6709
+ }
6710
+ this.collectionReferences = result;
6711
+ return result;
6712
+ };
6110
6713
  this.getIndexDefinitions = async (level) => {
6111
6714
  if (!this.collectionIndexDefinitions) {
6112
6715
  await new Promise(async (resolve2, reject) => {
@@ -6116,7 +6719,23 @@ var Database = class {
6116
6719
  const collections = schema.getCollections();
6117
6720
  for (const collection of collections) {
6118
6721
  const indexDefinitions = {
6119
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6722
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6723
+ // provide a default sort key which is the file sort
6724
+ // pseudo-index for the collection's references
6725
+ [REFS_COLLECTIONS_SORT_KEY]: {
6726
+ fields: [
6727
+ {
6728
+ name: REFS_REFERENCE_FIELD,
6729
+ type: "string",
6730
+ list: false
6731
+ },
6732
+ {
6733
+ name: REFS_PATH_FIELD,
6734
+ type: "string",
6735
+ list: false
6736
+ }
6737
+ ]
6738
+ }
6120
6739
  };
6121
6740
  if (collection.fields) {
6122
6741
  for (const field of collection.fields) {
@@ -6268,29 +6887,36 @@ var Database = class {
6268
6887
  }
6269
6888
  startKey = startKey || key || "";
6270
6889
  endKey = key || "";
6271
- edges = [...edges, { cursor: key, path: filepath }];
6890
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6272
6891
  }
6273
6892
  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
- });
6893
+ edges: await sequential(
6894
+ edges,
6895
+ async ({
6896
+ cursor,
6897
+ path: path7,
6898
+ value
6899
+ }) => {
6900
+ try {
6901
+ const node = await hydrator(path7, value);
6902
+ return {
6903
+ node,
6904
+ cursor: btoa(cursor)
6905
+ };
6906
+ } catch (error) {
6907
+ console.log(error);
6908
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6909
+ throw new TinaQueryError({
6910
+ originalError: error,
6911
+ file: path7,
6912
+ collection: collection.name,
6913
+ stack: error.stack
6914
+ });
6915
+ }
6916
+ throw error;
6290
6917
  }
6291
- throw error;
6292
6918
  }
6293
- }),
6919
+ ),
6294
6920
  pageInfo: {
6295
6921
  hasPreviousPage,
6296
6922
  hasNextPage,
@@ -6315,7 +6941,7 @@ var Database = class {
6315
6941
  try {
6316
6942
  lookup = lookupFromLockFile || JSON.parse(
6317
6943
  await this.bridge.get(
6318
- (0, import_schema_tools3.normalizePath)(
6944
+ (0, import_schema_tools4.normalizePath)(
6319
6945
  import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")
6320
6946
  )
6321
6947
  )
@@ -6340,15 +6966,15 @@ var Database = class {
6340
6966
  }
6341
6967
  const contentRootLevel = nextLevel.sublevel(CONTENT_ROOT_PREFIX, SUBLEVEL_OPTIONS);
6342
6968
  await contentRootLevel.put(
6343
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_graphql.json")),
6969
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_graphql.json")),
6344
6970
  graphQLSchema
6345
6971
  );
6346
6972
  await contentRootLevel.put(
6347
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_schema.json")),
6973
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_schema.json")),
6348
6974
  tinaSchema.schema
6349
6975
  );
6350
6976
  await contentRootLevel.put(
6351
- (0, import_schema_tools3.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")),
6977
+ (0, import_schema_tools4.normalizePath)(import_node_path.default.join(this.getGeneratedFolder(), "_lookup.json")),
6352
6978
  lookup
6353
6979
  );
6354
6980
  const result = await this._indexAllContent(
@@ -6430,23 +7056,25 @@ var Database = class {
6430
7056
  }
6431
7057
  };
6432
7058
  this.delete = async (filepath) => {
7059
+ var _a;
6433
7060
  await this.initLevel();
6434
7061
  const collection = await this.collectionForPath(filepath);
6435
7062
  if (!collection) {
6436
7063
  throw new Error(`No collection found for path: ${filepath}`);
6437
7064
  }
6438
7065
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7066
+ const collectionReferences = (_a = await this.getCollectionReferences()) == null ? void 0 : _a[collection.name];
6439
7067
  const collectionIndexDefinitions = indexDefinitions == null ? void 0 : indexDefinitions[collection.name];
6440
7068
  let level = this.contentLevel;
6441
7069
  if (collection == null ? void 0 : collection.isDetached) {
6442
7070
  level = this.appLevel.sublevel(collection == null ? void 0 : collection.name, SUBLEVEL_OPTIONS);
6443
7071
  }
6444
- const itemKey = (0, import_schema_tools3.normalizePath)(filepath);
7072
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
6445
7073
  const rootSublevel = level.sublevel(
6446
7074
  CONTENT_ROOT_PREFIX,
6447
7075
  SUBLEVEL_OPTIONS
6448
7076
  );
6449
- const item = await rootSublevel.get(itemKey);
7077
+ const item = await rootSublevel.get(normalizedPath);
6450
7078
  if (item) {
6451
7079
  const folderTreeBuilder = new FolderTreeBuilder();
6452
7080
  const folderKey = folderTreeBuilder.update(
@@ -6454,16 +7082,25 @@ var Database = class {
6454
7082
  collection.path || ""
6455
7083
  );
6456
7084
  await this.contentLevel.batch([
7085
+ ...makeRefOpsForDocument(
7086
+ normalizedPath,
7087
+ collection.name,
7088
+ collectionReferences,
7089
+ item,
7090
+ "del",
7091
+ level
7092
+ ),
6457
7093
  ...makeIndexOpsForDocument(
6458
- filepath,
7094
+ normalizedPath,
6459
7095
  collection.name,
6460
7096
  collectionIndexDefinitions,
6461
7097
  item,
6462
7098
  "del",
6463
7099
  level
6464
7100
  ),
7101
+ // folder indices
6465
7102
  ...makeIndexOpsForDocument(
6466
- filepath,
7103
+ normalizedPath,
6467
7104
  `${collection.name}_${folderKey}`,
6468
7105
  collectionIndexDefinitions,
6469
7106
  item,
@@ -6472,17 +7109,17 @@ var Database = class {
6472
7109
  ),
6473
7110
  {
6474
7111
  type: "del",
6475
- key: itemKey,
7112
+ key: normalizedPath,
6476
7113
  sublevel: rootSublevel
6477
7114
  }
6478
7115
  ]);
6479
7116
  }
6480
7117
  if (!(collection == null ? void 0 : collection.isDetached)) {
6481
7118
  if (this.bridge) {
6482
- await this.bridge.delete((0, import_schema_tools3.normalizePath)(filepath));
7119
+ await this.bridge.delete(normalizedPath);
6483
7120
  }
6484
7121
  try {
6485
- await this.onDelete((0, import_schema_tools3.normalizePath)(filepath));
7122
+ await this.onDelete(normalizedPath);
6486
7123
  } catch (e) {
6487
7124
  throw new import_graphql6.GraphQLError(
6488
7125
  `Error running onDelete hook for ${filepath}: ${e}`,
@@ -6530,7 +7167,13 @@ var Database = class {
6530
7167
  );
6531
7168
  }
6532
7169
  } else {
6533
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7170
+ await _indexContent(
7171
+ this,
7172
+ level,
7173
+ contentPaths,
7174
+ enqueueOps,
7175
+ collection
7176
+ );
6534
7177
  }
6535
7178
  }
6536
7179
  );
@@ -6616,6 +7259,9 @@ var Database = class {
6616
7259
  info: templateInfo
6617
7260
  };
6618
7261
  }
7262
+ /**
7263
+ * Clears the internal cache of the tinaSchema and the lookup file. This allows the state to be reset
7264
+ */
6619
7265
  clearCache() {
6620
7266
  this.tinaSchema = null;
6621
7267
  this._lookup = null;
@@ -6667,6 +7313,7 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
6667
7313
  );
6668
7314
  var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${(collection == null ? void 0 : collection.format) || "md"}`);
6669
7315
  var _indexContent = async (database, level, documentPaths, enqueueOps, collection, passwordFields) => {
7316
+ var _a;
6670
7317
  let collectionIndexDefinitions;
6671
7318
  let collectionPath;
6672
7319
  if (collection) {
@@ -6677,14 +7324,16 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6677
7324
  }
6678
7325
  collectionPath = collection.path;
6679
7326
  }
7327
+ const collectionReferences = (_a = await database.getCollectionReferences()) == null ? void 0 : _a[collection == null ? void 0 : collection.name];
6680
7328
  const tinaSchema = await database.getSchema();
6681
7329
  let templateInfo = null;
6682
7330
  if (collection) {
6683
7331
  templateInfo = tinaSchema.getTemplatesForCollectable(collection);
6684
7332
  }
6685
7333
  const folderTreeBuilder = new FolderTreeBuilder();
6686
- await sequential(documentPaths, async (filepath) => {
7334
+ async function processFile(filepath) {
6687
7335
  try {
7336
+ console.log(`Preloading ${filepath}`);
6688
7337
  const aliasedData = await loadAndParseWithAliases(
6689
7338
  database.bridge,
6690
7339
  filepath,
@@ -6697,13 +7346,63 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6697
7346
  if (passwordFields == null ? void 0 : passwordFields.length) {
6698
7347
  await hashPasswordValues(aliasedData, passwordFields);
6699
7348
  }
6700
- const normalizedPath = (0, import_schema_tools3.normalizePath)(filepath);
7349
+ const normalizedPath = (0, import_schema_tools4.normalizePath)(filepath);
7350
+ const rootSublevel = level.sublevel(
7351
+ CONTENT_ROOT_PREFIX,
7352
+ SUBLEVEL_OPTIONS
7353
+ );
6701
7354
  const folderKey = folderTreeBuilder.update(
6702
7355
  normalizedPath,
6703
7356
  collectionPath || ""
6704
7357
  );
7358
+ const item = await rootSublevel.get(normalizedPath);
7359
+ await new Promise((res) => setTimeout(res, 5e3));
7360
+ console.log("Ceasing preload");
7361
+ if (item) {
7362
+ console.log("Running deletion operations");
7363
+ await database.contentLevel.batch([
7364
+ ...makeRefOpsForDocument(
7365
+ normalizedPath,
7366
+ collection == null ? void 0 : collection.name,
7367
+ collectionReferences,
7368
+ item,
7369
+ "del",
7370
+ level
7371
+ ),
7372
+ ...makeIndexOpsForDocument(
7373
+ normalizedPath,
7374
+ collection.name,
7375
+ collectionIndexDefinitions,
7376
+ item,
7377
+ "del",
7378
+ level
7379
+ ),
7380
+ // folder indices
7381
+ ...makeIndexOpsForDocument(
7382
+ normalizedPath,
7383
+ `${collection.name}_${folderKey}`,
7384
+ collectionIndexDefinitions,
7385
+ item,
7386
+ "del",
7387
+ level
7388
+ ),
7389
+ {
7390
+ type: "del",
7391
+ key: normalizedPath,
7392
+ sublevel: rootSublevel
7393
+ }
7394
+ ]);
7395
+ }
6705
7396
  if (!isGitKeep(filepath, collection)) {
6706
7397
  await enqueueOps([
7398
+ ...makeRefOpsForDocument(
7399
+ normalizedPath,
7400
+ collection == null ? void 0 : collection.name,
7401
+ collectionReferences,
7402
+ aliasedData,
7403
+ "put",
7404
+ level
7405
+ ),
6707
7406
  ...makeIndexOpsForDocument(
6708
7407
  normalizedPath,
6709
7408
  collection == null ? void 0 : collection.name,
@@ -6712,6 +7411,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6712
7411
  "put",
6713
7412
  level
6714
7413
  ),
7414
+ // folder indexes
6715
7415
  ...makeIndexOpsForDocument(
6716
7416
  normalizedPath,
6717
7417
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -6739,7 +7439,10 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6739
7439
  stack: error.stack
6740
7440
  });
6741
7441
  }
6742
- });
7442
+ }
7443
+ ;
7444
+ const filePromises = documentPaths.map(processFile);
7445
+ await Promise.all(filePromises);
6743
7446
  if (collection) {
6744
7447
  await enqueueOps(
6745
7448
  makeFolderOpsForCollection(
@@ -6753,6 +7456,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6753
7456
  }
6754
7457
  };
6755
7458
  var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection) => {
7459
+ var _a;
6756
7460
  if (!documentPaths.length) {
6757
7461
  return;
6758
7462
  }
@@ -6766,6 +7470,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6766
7470
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6767
7471
  }
6768
7472
  }
7473
+ const collectionReferences = (_a = await database.getCollectionReferences()) == null ? void 0 : _a[collection == null ? void 0 : collection.name];
6769
7474
  const tinaSchema = await database.getSchema();
6770
7475
  let templateInfo = null;
6771
7476
  if (collection) {
@@ -6777,7 +7482,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6777
7482
  );
6778
7483
  const folderTreeBuilder = new FolderTreeBuilder();
6779
7484
  await sequential(documentPaths, async (filepath) => {
6780
- const itemKey = (0, import_schema_tools3.normalizePath)(filepath);
7485
+ const itemKey = (0, import_schema_tools4.normalizePath)(filepath);
6781
7486
  const item = await rootLevel.get(itemKey);
6782
7487
  if (item) {
6783
7488
  const folderKey = folderTreeBuilder.update(
@@ -6789,6 +7494,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6789
7494
  item
6790
7495
  ) : item;
6791
7496
  await enqueueOps([
7497
+ ...makeRefOpsForDocument(
7498
+ itemKey,
7499
+ collection == null ? void 0 : collection.name,
7500
+ collectionReferences,
7501
+ aliasedData,
7502
+ "del",
7503
+ database.contentLevel
7504
+ ),
6792
7505
  ...makeIndexOpsForDocument(
6793
7506
  itemKey,
6794
7507
  collection.name,
@@ -6797,6 +7510,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6797
7510
  "del",
6798
7511
  database.contentLevel
6799
7512
  ),
7513
+ // folder indexes
6800
7514
  ...makeIndexOpsForDocument(
6801
7515
  itemKey,
6802
7516
  `${collection == null ? void 0 : collection.name}_${folderKey}`,
@@ -6863,14 +7577,14 @@ var getChangedFiles = async ({
6863
7577
  const rootDir = await findGitRoot(dir);
6864
7578
  let pathPrefix = "";
6865
7579
  if (rootDir !== dir) {
6866
- pathPrefix = (0, import_schema_tools3.normalizePath)(dir.substring(rootDir.length + 1));
7580
+ pathPrefix = (0, import_schema_tools4.normalizePath)(dir.substring(rootDir.length + 1));
6867
7581
  }
6868
7582
  await import_isomorphic_git.default.walk({
6869
7583
  fs: fs4,
6870
7584
  dir: rootDir,
6871
7585
  trees: [import_isomorphic_git.default.TREE({ ref: from }), import_isomorphic_git.default.TREE({ ref: to })],
6872
7586
  map: async function(filename, [A, B]) {
6873
- const relativePath = (0, import_schema_tools3.normalizePath)(filename).substring(pathPrefix.length);
7587
+ const relativePath = (0, import_schema_tools4.normalizePath)(filename).substring(pathPrefix.length);
6874
7588
  let matches = false;
6875
7589
  for (const [key, matcher] of Object.entries(pathFilter)) {
6876
7590
  if (relativePath.startsWith(key)) {
@@ -7012,17 +7726,26 @@ var IsomorphicBridge = class {
7012
7726
  getAuthor() {
7013
7727
  return {
7014
7728
  ...this.author,
7015
- timestamp: Math.round(new Date().getTime() / 1e3),
7729
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
7016
7730
  timezoneOffset: 0
7017
7731
  };
7018
7732
  }
7019
7733
  getCommitter() {
7020
7734
  return {
7021
7735
  ...this.committer,
7022
- timestamp: Math.round(new Date().getTime() / 1e3),
7736
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
7023
7737
  timezoneOffset: 0
7024
7738
  };
7025
7739
  }
7740
+ /**
7741
+ * Recursively populate paths matching `pattern` for the given `entry`
7742
+ *
7743
+ * @param pattern - pattern to filter paths by
7744
+ * @param entry - TreeEntry to start building list from
7745
+ * @param path - base path
7746
+ * @param results
7747
+ * @private
7748
+ */
7026
7749
  async listEntries({
7027
7750
  pattern,
7028
7751
  entry,
@@ -7055,6 +7778,15 @@ var IsomorphicBridge = class {
7055
7778
  });
7056
7779
  }
7057
7780
  }
7781
+ /**
7782
+ * For the specified path, returns an object with an array containing the parts of the path (pathParts)
7783
+ * and an array containing the WalkerEntry objects for the path parts (pathEntries). Any null elements in the
7784
+ * pathEntries are placeholders for non-existent entries.
7785
+ *
7786
+ * @param path - path being resolved
7787
+ * @param ref - ref to resolve path entries for
7788
+ * @private
7789
+ */
7058
7790
  async resolvePathEntries(path7, ref) {
7059
7791
  let pathParts = path7.split("/");
7060
7792
  const result = await import_isomorphic_git2.default.walk({
@@ -7085,6 +7817,17 @@ var IsomorphicBridge = class {
7085
7817
  }
7086
7818
  return { pathParts, pathEntries };
7087
7819
  }
7820
+ /**
7821
+ * Updates tree entry and associated parent tree entries
7822
+ *
7823
+ * @param existingOid - the existing OID
7824
+ * @param updatedOid - the updated OID
7825
+ * @param path - the path of the entry being updated
7826
+ * @param type - the type of the entry being updated (blob or tree)
7827
+ * @param pathEntries - parent path entries
7828
+ * @param pathParts - parent path parts
7829
+ * @private
7830
+ */
7088
7831
  async updateTreeHierarchy(existingOid, updatedOid, path7, type, pathEntries, pathParts) {
7089
7832
  const lastIdx = pathEntries.length - 1;
7090
7833
  const parentEntry = pathEntries[lastIdx];
@@ -7140,6 +7883,13 @@ var IsomorphicBridge = class {
7140
7883
  );
7141
7884
  }
7142
7885
  }
7886
+ /**
7887
+ * Creates a commit for the specified tree and updates the specified ref to point to the commit
7888
+ *
7889
+ * @param treeSha - sha of the new tree
7890
+ * @param ref - the ref that should be updated
7891
+ * @private
7892
+ */
7143
7893
  async commitTree(treeSha, ref) {
7144
7894
  const commitSha = await import_isomorphic_git2.default.writeCommit({
7145
7895
  ...this.isomorphicConfig,
@@ -7152,6 +7902,7 @@ var IsomorphicBridge = class {
7152
7902
  })
7153
7903
  ],
7154
7904
  message: this.commitMessage,
7905
+ // TODO these should be configurable
7155
7906
  author: this.getAuthor(),
7156
7907
  committer: this.getCommitter()
7157
7908
  }
@@ -7390,5 +8141,5 @@ var buildSchema = async (config, flags) => {
7390
8141
  transformDocument,
7391
8142
  transformDocumentIntoPayload
7392
8143
  });
7393
- //! Replaces _.flattenDeep()
7394
8144
  //! Replaces _.get()
8145
+ //! Replaces _.flattenDeep()