@tinacms/graphql 0.0.0-ef282d9-20241024212433 → 0.0.0-f2577b9-20251119082459

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.mjs CHANGED
@@ -68,6 +68,15 @@ var SysFieldDefinition = {
68
68
  selectionSet: {
69
69
  kind: "SelectionSet",
70
70
  selections: [
71
+ // {
72
+ // kind: 'Field' as const,
73
+ // name: {
74
+ // kind: 'Name' as const,
75
+ // value: 'title',
76
+ // },
77
+ // arguments: [],
78
+ // directives: [],
79
+ // },
71
80
  {
72
81
  kind: "Field",
73
82
  name: {
@@ -86,6 +95,15 @@ var SysFieldDefinition = {
86
95
  arguments: [],
87
96
  directives: []
88
97
  },
98
+ {
99
+ kind: "Field",
100
+ name: {
101
+ kind: "Name",
102
+ value: "hasReferences"
103
+ },
104
+ arguments: [],
105
+ directives: []
106
+ },
89
107
  {
90
108
  kind: "Field",
91
109
  name: {
@@ -126,6 +144,10 @@ var SysFieldDefinition = {
126
144
  }
127
145
  };
128
146
  var astBuilder = {
147
+ /**
148
+ * `FormFieldBuilder` acts as a shortcut to building an entire `ObjectTypeDefinition`, we use this
149
+ * because all Tina field objects share a common set of fields ('name', 'label', 'component')
150
+ */
129
151
  FormFieldBuilder: ({
130
152
  name,
131
153
  additionalFields
@@ -349,6 +371,8 @@ var astBuilder = {
349
371
  kind: "Name",
350
372
  value: name
351
373
  },
374
+ // @ts-ignore FIXME; this is being handled properly but we're lying to
375
+ // ts and then fixing it in the `extractInlineTypes` function
352
376
  fields
353
377
  }),
354
378
  UnionTypeDefinition: ({
@@ -361,6 +385,8 @@ var astBuilder = {
361
385
  value: name
362
386
  },
363
387
  directives: [],
388
+ // @ts-ignore FIXME; this is being handled properly but we're lying to
389
+ // ts and then fixing it in the `extractInlineTypes` function
364
390
  types: types.map((name2) => ({
365
391
  kind: "NamedType",
366
392
  name: {
@@ -457,8 +483,11 @@ var astBuilder = {
457
483
  string: "String",
458
484
  boolean: "Boolean",
459
485
  number: "Float",
486
+ // FIXME - needs to be float or int
460
487
  datetime: "String",
488
+ // FIXME
461
489
  image: "String",
490
+ // FIXME
462
491
  text: "String"
463
492
  };
464
493
  return scalars[type];
@@ -957,8 +986,7 @@ var astBuilder = {
957
986
  }
958
987
  };
959
988
  var capitalize = (s) => {
960
- if (typeof s !== "string")
961
- return "";
989
+ if (typeof s !== "string") return "";
962
990
  return s.charAt(0).toUpperCase() + s.slice(1);
963
991
  };
964
992
  var extractInlineTypes = (item) => {
@@ -1001,41 +1029,6 @@ function* walk(maybeNode, visited = /* @__PURE__ */ new WeakSet()) {
1001
1029
  yield maybeNode;
1002
1030
  visited.add(maybeNode);
1003
1031
  }
1004
- function addNamespaceToSchema(maybeNode, namespace = []) {
1005
- if (typeof maybeNode === "string") {
1006
- return maybeNode;
1007
- }
1008
- if (typeof maybeNode === "boolean") {
1009
- return maybeNode;
1010
- }
1011
- const newNode = maybeNode;
1012
- const keys = Object.keys(maybeNode);
1013
- Object.values(maybeNode).map((m, index) => {
1014
- const key = keys[index];
1015
- if (Array.isArray(m)) {
1016
- newNode[key] = m.map((element) => {
1017
- if (!element) {
1018
- return;
1019
- }
1020
- if (!element.hasOwnProperty("name")) {
1021
- return element;
1022
- }
1023
- const value = element.name || element.value;
1024
- return addNamespaceToSchema(element, [...namespace, value]);
1025
- });
1026
- } else {
1027
- if (!m) {
1028
- return;
1029
- }
1030
- if (!m.hasOwnProperty("name")) {
1031
- newNode[key] = m;
1032
- } else {
1033
- newNode[key] = addNamespaceToSchema(m, [...namespace, m.name]);
1034
- }
1035
- }
1036
- });
1037
- return { ...newNode, namespace };
1038
- }
1039
1032
  var generateNamespacedFieldName = (names, suffix = "") => {
1040
1033
  return (suffix ? [...names, suffix] : names).map(capitalize).join("");
1041
1034
  };
@@ -1195,6 +1188,11 @@ var scalarDefinitions = [
1195
1188
  required: true,
1196
1189
  type: astBuilder.TYPES.String
1197
1190
  }),
1191
+ astBuilder.FieldDefinition({
1192
+ name: "hasReferences",
1193
+ required: false,
1194
+ type: astBuilder.TYPES.Boolean
1195
+ }),
1198
1196
  astBuilder.FieldDefinition({
1199
1197
  name: "breadcrumbs",
1200
1198
  required: true,
@@ -1408,6 +1406,19 @@ var Builder = class {
1408
1406
  this.addToLookupMap = (lookup) => {
1409
1407
  this.lookupMap[lookup.type] = lookup;
1410
1408
  };
1409
+ /**
1410
+ * ```graphql
1411
+ * # ex.
1412
+ * {
1413
+ * getCollection(collection: $collection) {
1414
+ * name
1415
+ * documents {...}
1416
+ * }
1417
+ * }
1418
+ * ```
1419
+ *
1420
+ * @param collections
1421
+ */
1411
1422
  this.buildCollectionDefinition = async (collections) => {
1412
1423
  const name = "collection";
1413
1424
  const typeName = "Collection";
@@ -1478,6 +1489,19 @@ var Builder = class {
1478
1489
  required: true
1479
1490
  });
1480
1491
  };
1492
+ /**
1493
+ * ```graphql
1494
+ * # ex.
1495
+ * {
1496
+ * getCollections {
1497
+ * name
1498
+ * documents {...}
1499
+ * }
1500
+ * }
1501
+ * ```
1502
+ *
1503
+ * @param collections
1504
+ */
1481
1505
  this.buildMultiCollectionDefinition = async (collections) => {
1482
1506
  const name = "collections";
1483
1507
  const typeName = "Collection";
@@ -1488,6 +1512,17 @@ var Builder = class {
1488
1512
  required: true
1489
1513
  });
1490
1514
  };
1515
+ /**
1516
+ * ```graphql
1517
+ * # ex.
1518
+ * {
1519
+ * node(id: $id) {
1520
+ * id
1521
+ * data {...}
1522
+ * }
1523
+ * }
1524
+ * ```
1525
+ */
1491
1526
  this.multiNodeDocument = async () => {
1492
1527
  const name = "node";
1493
1528
  const args = [
@@ -1508,6 +1543,19 @@ var Builder = class {
1508
1543
  required: true
1509
1544
  });
1510
1545
  };
1546
+ /**
1547
+ * ```graphql
1548
+ * # ex.
1549
+ * {
1550
+ * getDocument(collection: $collection, relativePath: $relativePath) {
1551
+ * id
1552
+ * data {...}
1553
+ * }
1554
+ * }
1555
+ * ```
1556
+ *
1557
+ * @param collections
1558
+ */
1511
1559
  this.multiCollectionDocument = async (collections) => {
1512
1560
  const name = "document";
1513
1561
  const args = [
@@ -1533,6 +1581,19 @@ var Builder = class {
1533
1581
  required: true
1534
1582
  });
1535
1583
  };
1584
+ /**
1585
+ * ```graphql
1586
+ * # ex.
1587
+ * {
1588
+ * addPendingDocument(collection: $collection, relativePath: $relativePath, params: $params) {
1589
+ * id
1590
+ * data {...}
1591
+ * }
1592
+ * }
1593
+ * ```
1594
+ *
1595
+ * @param collections
1596
+ */
1536
1597
  this.addMultiCollectionDocumentMutation = async () => {
1537
1598
  return astBuilder.FieldDefinition({
1538
1599
  name: "addPendingDocument",
@@ -1557,6 +1618,19 @@ var Builder = class {
1557
1618
  type: astBuilder.TYPES.MultiCollectionDocument
1558
1619
  });
1559
1620
  };
1621
+ /**
1622
+ * ```graphql
1623
+ * # ex.
1624
+ * {
1625
+ * createDocument(relativePath: $relativePath, params: $params) {
1626
+ * id
1627
+ * data {...}
1628
+ * }
1629
+ * }
1630
+ * ```
1631
+ *
1632
+ * @param collections
1633
+ */
1560
1634
  this.buildCreateCollectionDocumentMutation = async (collections) => {
1561
1635
  return astBuilder.FieldDefinition({
1562
1636
  name: "createDocument",
@@ -1584,6 +1658,19 @@ var Builder = class {
1584
1658
  type: astBuilder.TYPES.MultiCollectionDocument
1585
1659
  });
1586
1660
  };
1661
+ /**
1662
+ * ```graphql
1663
+ * # ex.
1664
+ * {
1665
+ * updateDocument(relativePath: $relativePath, params: $params) {
1666
+ * id
1667
+ * data {...}
1668
+ * }
1669
+ * }
1670
+ * ```
1671
+ *
1672
+ * @param collections
1673
+ */
1587
1674
  this.buildUpdateCollectionDocumentMutation = async (collections) => {
1588
1675
  return astBuilder.FieldDefinition({
1589
1676
  name: "updateDocument",
@@ -1611,6 +1698,19 @@ var Builder = class {
1611
1698
  type: astBuilder.TYPES.MultiCollectionDocument
1612
1699
  });
1613
1700
  };
1701
+ /**
1702
+ * ```graphql
1703
+ * # ex.
1704
+ * {
1705
+ * deleteDocument(relativePath: $relativePath, params: $params) {
1706
+ * id
1707
+ * data {...}
1708
+ * }
1709
+ * }
1710
+ * ```
1711
+ *
1712
+ * @param collections
1713
+ */
1614
1714
  this.buildDeleteCollectionDocumentMutation = async (collections) => {
1615
1715
  return astBuilder.FieldDefinition({
1616
1716
  name: "deleteDocument",
@@ -1630,6 +1730,19 @@ var Builder = class {
1630
1730
  type: astBuilder.TYPES.MultiCollectionDocument
1631
1731
  });
1632
1732
  };
1733
+ /**
1734
+ * ```graphql
1735
+ * # ex.
1736
+ * {
1737
+ * createFolder(folderName: $folderName, params: $params) {
1738
+ * id
1739
+ * data {...}
1740
+ * }
1741
+ * }
1742
+ * ```
1743
+ *
1744
+ * @param collections
1745
+ */
1633
1746
  this.buildCreateCollectionFolderMutation = async () => {
1634
1747
  return astBuilder.FieldDefinition({
1635
1748
  name: "createFolder",
@@ -1649,6 +1762,19 @@ var Builder = class {
1649
1762
  type: astBuilder.TYPES.MultiCollectionDocument
1650
1763
  });
1651
1764
  };
1765
+ /**
1766
+ * ```graphql
1767
+ * # ex.
1768
+ * {
1769
+ * getPostDocument(relativePath: $relativePath) {
1770
+ * id
1771
+ * data {...}
1772
+ * }
1773
+ * }
1774
+ * ```
1775
+ *
1776
+ * @param collection
1777
+ */
1652
1778
  this.collectionDocument = async (collection) => {
1653
1779
  const name = NAMER.queryName([collection.name]);
1654
1780
  const type = await this._buildCollectionDocumentType(collection);
@@ -1709,6 +1835,20 @@ var Builder = class {
1709
1835
  const args = [];
1710
1836
  return astBuilder.FieldDefinition({ type, name, args, required: false });
1711
1837
  };
1838
+ /**
1839
+ * Turns a collection into a fragment that gets updated on build. This fragment does not resolve references
1840
+ * ```graphql
1841
+ * # ex.
1842
+ * fragment AuthorsParts on Authors {
1843
+ * name
1844
+ * avatar
1845
+ * ...
1846
+ * }
1847
+ * ```
1848
+ *
1849
+ * @public
1850
+ * @param collection a TinaCloud collection
1851
+ */
1712
1852
  this.collectionFragment = async (collection) => {
1713
1853
  const name = NAMER.dataTypeName(collection.namespace);
1714
1854
  const fragmentName = NAMER.fragmentName(collection.namespace);
@@ -1722,6 +1862,20 @@ var Builder = class {
1722
1862
  selections: filterSelections(selections)
1723
1863
  });
1724
1864
  };
1865
+ /**
1866
+ * Given a collection this function returns its selections set. For example for Post this would return
1867
+ *
1868
+ * "
1869
+ * body
1870
+ * title
1871
+ * ... on Author {
1872
+ * name
1873
+ * heroImg
1874
+ * }
1875
+ *
1876
+ * But in the AST format
1877
+ *
1878
+ * */
1725
1879
  this._getCollectionFragmentSelections = async (collection, depth) => {
1726
1880
  const selections = [];
1727
1881
  selections.push({
@@ -1803,9 +1957,9 @@ var Builder = class {
1803
1957
  ]
1804
1958
  });
1805
1959
  }
1960
+ // TODO: Should we throw here?
1806
1961
  case "reference":
1807
- if (depth >= this.maxDepth)
1808
- return false;
1962
+ if (depth >= this.maxDepth) return false;
1809
1963
  if (!("collections" in field)) {
1810
1964
  return false;
1811
1965
  }
@@ -1837,6 +1991,7 @@ var Builder = class {
1837
1991
  name: field.name,
1838
1992
  selections: [
1839
1993
  ...selections,
1994
+ // This is ... on Document { id }
1840
1995
  {
1841
1996
  kind: "InlineFragment",
1842
1997
  typeCondition: {
@@ -1867,6 +2022,19 @@ var Builder = class {
1867
2022
  });
1868
2023
  }
1869
2024
  };
2025
+ /**
2026
+ * ```graphql
2027
+ * # ex.
2028
+ * mutation {
2029
+ * updatePostDocument(relativePath: $relativePath, params: $params) {
2030
+ * id
2031
+ * data {...}
2032
+ * }
2033
+ * }
2034
+ * ```
2035
+ *
2036
+ * @param collection
2037
+ */
1870
2038
  this.updateCollectionDocumentMutation = async (collection) => {
1871
2039
  return astBuilder.FieldDefinition({
1872
2040
  type: await this._buildCollectionDocumentType(collection),
@@ -1886,6 +2054,19 @@ var Builder = class {
1886
2054
  ]
1887
2055
  });
1888
2056
  };
2057
+ /**
2058
+ * ```graphql
2059
+ * # ex.
2060
+ * mutation {
2061
+ * createPostDocument(relativePath: $relativePath, params: $params) {
2062
+ * id
2063
+ * data {...}
2064
+ * }
2065
+ * }
2066
+ * ```
2067
+ *
2068
+ * @param collection
2069
+ */
1889
2070
  this.createCollectionDocumentMutation = async (collection) => {
1890
2071
  return astBuilder.FieldDefinition({
1891
2072
  type: await this._buildCollectionDocumentType(collection),
@@ -1905,6 +2086,22 @@ var Builder = class {
1905
2086
  ]
1906
2087
  });
1907
2088
  };
2089
+ /**
2090
+ * ```graphql
2091
+ * # ex.
2092
+ * {
2093
+ * getPostList(first: 10) {
2094
+ * edges {
2095
+ * node {
2096
+ * id
2097
+ * }
2098
+ * }
2099
+ * }
2100
+ * }
2101
+ * ```
2102
+ *
2103
+ * @param collection
2104
+ */
1908
2105
  this.collectionDocumentList = async (collection) => {
1909
2106
  const connectionName = NAMER.referenceConnectionType(collection.namespace);
1910
2107
  this.addToLookupMap({
@@ -1920,6 +2117,10 @@ var Builder = class {
1920
2117
  collection
1921
2118
  });
1922
2119
  };
2120
+ /**
2121
+ * GraphQL type definitions which remain unchanged regardless
2122
+ * of the supplied Tina schema. Ex. "node" interface
2123
+ */
1923
2124
  this.buildStaticDefinitions = () => staticDefinitions;
1924
2125
  this._buildCollectionDocumentType = async (collection, suffix = "", extraFields = [], extraInterfaces = []) => {
1925
2126
  const documentTypeName = NAMER.documentTypeName(collection.namespace);
@@ -2424,6 +2625,7 @@ var Builder = class {
2424
2625
  name: NAMER.dataFilterTypeName(namespace),
2425
2626
  fields: await sequential(collections, async (collection2) => {
2426
2627
  return astBuilder.InputValueDefinition({
2628
+ // @ts-ignore
2427
2629
  name: collection2.name,
2428
2630
  type: NAMER.dataFilterTypeName(collection2.namespace)
2429
2631
  });
@@ -2509,7 +2711,7 @@ var Builder = class {
2509
2711
  this._buildDataField = async (field) => {
2510
2712
  const listWarningMsg = `
2511
2713
  WARNING: The user interface for ${field.type} does not support \`list: true\`
2512
- Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2714
+ Visit https://tina.io/docs/r/content-fields/#list-fields/ for more information
2513
2715
 
2514
2716
  `;
2515
2717
  switch (field.type) {
@@ -2612,7 +2814,8 @@ Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2612
2814
  ]
2613
2815
  });
2614
2816
  };
2615
- this.maxDepth = config?.tinaSchema.schema?.config?.client?.referenceDepth ?? 2;
2817
+ this.maxDepth = // @ts-ignore
2818
+ config?.tinaSchema.schema?.config?.client?.referenceDepth ?? 2;
2616
2819
  this.tinaSchema = config.tinaSchema;
2617
2820
  this.lookupMap = {};
2618
2821
  }
@@ -2623,8 +2826,7 @@ Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2623
2826
  selections.push(field);
2624
2827
  });
2625
2828
  const filteredSelections = filterSelections(selections);
2626
- if (!filteredSelections.length)
2627
- return false;
2829
+ if (!filteredSelections.length) return false;
2628
2830
  return astBuilder.InlineFragmentDefinition({
2629
2831
  selections: filteredSelections,
2630
2832
  name: NAMER.dataTypeName(template.namespace)
@@ -2661,6 +2863,7 @@ var filterSelections = (arr) => {
2661
2863
  import { TinaSchema } from "@tinacms/schema-tools";
2662
2864
 
2663
2865
  // src/schema/validate.ts
2866
+ import { addNamespaceToSchema } from "@tinacms/schema-tools";
2664
2867
  import deepClone from "lodash.clonedeep";
2665
2868
  import * as yup2 from "yup";
2666
2869
  import {
@@ -2707,6 +2910,7 @@ var validationCollectionsPathAndMatch = (collections) => {
2707
2910
  }).map((x) => `${x.path}${x.format || "md"}`);
2708
2911
  if (noMatchCollections.length !== new Set(noMatchCollections).size) {
2709
2912
  throw new Error(
2913
+ // TODO: add a link to the docs
2710
2914
  "Two collections without match can not have the same `path`. Please make the `path` unique or add a matches property to the collection."
2711
2915
  );
2712
2916
  }
@@ -2815,7 +3019,7 @@ var validateField = async (field) => {
2815
3019
  // package.json
2816
3020
  var package_default = {
2817
3021
  name: "@tinacms/graphql",
2818
- version: "1.5.6",
3022
+ version: "1.6.1",
2819
3023
  main: "dist/index.js",
2820
3024
  module: "dist/index.mjs",
2821
3025
  typings: "dist/index.d.ts",
@@ -2841,33 +3045,32 @@ var package_default = {
2841
3045
  types: "pnpm tsc",
2842
3046
  build: "tinacms-scripts build",
2843
3047
  docs: "pnpm typedoc",
2844
- serve: "pnpm nodemon dist/server.js",
2845
- test: "jest",
2846
- "test-watch": "jest --watch"
3048
+ test: "vitest run",
3049
+ "test-watch": "vitest"
2847
3050
  },
2848
3051
  dependencies: {
2849
- "@iarna/toml": "^2.2.5",
3052
+ "@iarna/toml": "catalog:",
2850
3053
  "@tinacms/mdx": "workspace:*",
2851
3054
  "@tinacms/schema-tools": "workspace:*",
2852
- "abstract-level": "^1.0.4",
3055
+ "abstract-level": "catalog:",
2853
3056
  "date-fns": "^2.30.0",
2854
- "fast-glob": "^3.3.2",
2855
- "fs-extra": "^11.2.0",
2856
- "glob-parent": "^6.0.2",
3057
+ "fast-glob": "catalog:",
3058
+ "fs-extra": "catalog:",
3059
+ "glob-parent": "catalog:",
2857
3060
  graphql: "15.8.0",
2858
- "gray-matter": "^4.0.3",
2859
- "isomorphic-git": "^1.27.1",
2860
- "js-sha1": "^0.6.0",
3061
+ "gray-matter": "catalog:",
3062
+ "isomorphic-git": "catalog:",
3063
+ "js-sha1": "catalog:",
2861
3064
  "js-yaml": "^3.14.1",
2862
- "jsonpath-plus": "^6.0.1",
2863
- "lodash.clonedeep": "^4.5.0",
2864
- "lodash.set": "^4.3.2",
2865
- "lodash.uniqby": "^4.7.0",
2866
- "many-level": "^2.0.0",
2867
- micromatch: "4.0.8",
2868
- "normalize-path": "^3.0.0",
2869
- "readable-stream": "^4.5.2",
2870
- scmp: "^2.1.0",
3065
+ "jsonpath-plus": "catalog:",
3066
+ "lodash.clonedeep": "catalog:",
3067
+ "lodash.set": "catalog:",
3068
+ "lodash.uniqby": "catalog:",
3069
+ "many-level": "catalog:",
3070
+ micromatch: "catalog:",
3071
+ "normalize-path": "catalog:",
3072
+ "readable-stream": "catalog:",
3073
+ scmp: "catalog:",
2871
3074
  yup: "^0.32.11"
2872
3075
  },
2873
3076
  publishConfig: {
@@ -2882,26 +3085,24 @@ var package_default = {
2882
3085
  "@tinacms/scripts": "workspace:*",
2883
3086
  "@types/cors": "^2.8.17",
2884
3087
  "@types/estree": "^0.0.50",
2885
- "@types/express": "^4.17.21",
3088
+ "@types/express": "catalog:",
2886
3089
  "@types/fs-extra": "^9.0.13",
2887
- "@types/jest": "^26.0.24",
2888
3090
  "@types/js-yaml": "^3.12.10",
2889
- "@types/lodash.camelcase": "^4.3.9",
2890
- "@types/lodash.upperfirst": "^4.3.9",
2891
- "@types/lru-cache": "^5.1.1",
2892
- "@types/mdast": "^3.0.15",
2893
- "@types/micromatch": "^4.0.9",
2894
- "@types/node": "^22.7.4",
2895
- "@types/normalize-path": "^3.0.2",
2896
- "@types/ws": "^7.4.7",
3091
+ "@types/lodash.camelcase": "catalog:",
3092
+ "@types/lodash.upperfirst": "catalog:",
3093
+ "@types/lru-cache": "catalog:",
3094
+ "@types/mdast": "catalog:",
3095
+ "@types/micromatch": "catalog:",
3096
+ "@types/node": "^22.13.1",
3097
+ "@types/normalize-path": "catalog:",
3098
+ "@types/ws": "catalog:",
2897
3099
  "@types/yup": "^0.29.14",
2898
- jest: "^29.7.0",
2899
- "jest-diff": "^29.7.0",
2900
3100
  "jest-file-snapshot": "^0.5.0",
2901
- "jest-matcher-utils": "^29.7.0",
2902
- "memory-level": "^1.0.0",
2903
- nodemon: "3.1.4",
2904
- typescript: "^5.6.2"
3101
+ "memory-level": "catalog:",
3102
+ typescript: "^5.7.3",
3103
+ vite: "^4.5.9",
3104
+ vitest: "^0.32.4",
3105
+ zod: "catalog:"
2905
3106
  }
2906
3107
  };
2907
3108
 
@@ -2972,6 +3173,7 @@ var _buildFragments = async (builder, tinaSchema) => {
2972
3173
  const fragDoc = {
2973
3174
  kind: "Document",
2974
3175
  definitions: uniqBy2(
3176
+ // @ts-ignore
2975
3177
  extractInlineTypes(fragmentDefinitionsFields),
2976
3178
  (node) => node.name.value
2977
3179
  )
@@ -2994,6 +3196,7 @@ var _buildQueries = async (builder, tinaSchema) => {
2994
3196
  fragName,
2995
3197
  queryName: queryListName,
2996
3198
  filterType: queryFilterTypeName,
3199
+ // look for flag to see if the data layer is enabled
2997
3200
  dataLayer: Boolean(
2998
3201
  tinaSchema.config?.meta?.flags?.find((x) => x === "experimentalData")
2999
3202
  )
@@ -3003,6 +3206,7 @@ var _buildQueries = async (builder, tinaSchema) => {
3003
3206
  const queryDoc = {
3004
3207
  kind: "Document",
3005
3208
  definitions: uniqBy2(
3209
+ // @ts-ignore
3006
3210
  extractInlineTypes(operationsDefinitions),
3007
3211
  (node) => node.name.value
3008
3212
  )
@@ -3054,7 +3258,9 @@ var _buildSchema = async (builder, tinaSchema) => {
3054
3258
  await builder.buildCreateCollectionFolderMutation()
3055
3259
  );
3056
3260
  await sequential(collections, async (collection) => {
3057
- queryTypeDefinitionFields.push(await builder.collectionDocument(collection));
3261
+ queryTypeDefinitionFields.push(
3262
+ await builder.collectionDocument(collection)
3263
+ );
3058
3264
  if (collection.isAuthCollection) {
3059
3265
  queryTypeDefinitionFields.push(
3060
3266
  await builder.authenticationCollectionDocument(collection)
@@ -3091,6 +3297,7 @@ var _buildSchema = async (builder, tinaSchema) => {
3091
3297
  return {
3092
3298
  kind: "Document",
3093
3299
  definitions: uniqBy2(
3300
+ // @ts-ignore
3094
3301
  extractInlineTypes(definitions),
3095
3302
  (node) => node.name.value
3096
3303
  )
@@ -3103,394 +3310,158 @@ import { graphql, buildASTSchema, getNamedType, GraphQLError as GraphQLError4 }
3103
3310
  // src/resolver/index.ts
3104
3311
  import path3 from "path";
3105
3312
  import isValid from "date-fns/isValid/index.js";
3313
+ import { JSONPath as JSONPath2 } from "jsonpath-plus";
3106
3314
 
3107
3315
  // src/mdx/index.ts
3108
- import { parseMDX, stringifyMDX } from "@tinacms/mdx";
3316
+ import { parseMDX, serializeMDX } from "@tinacms/mdx";
3109
3317
 
3110
- // src/resolver/error.ts
3111
- var TinaGraphQLError = class extends Error {
3112
- constructor(message, extensions) {
3113
- super(message);
3114
- if (!this.name) {
3115
- Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
3116
- }
3117
- this.extensions = { ...extensions };
3118
- }
3318
+ // src/resolver/index.ts
3319
+ import { GraphQLError as GraphQLError2 } from "graphql";
3320
+
3321
+ // src/database/datalayer.ts
3322
+ import { JSONPath } from "jsonpath-plus";
3323
+ import sha from "js-sha1";
3324
+
3325
+ // src/database/level.ts
3326
+ var ARRAY_ITEM_VALUE_SEPARATOR = ",";
3327
+ var INDEX_KEY_FIELD_SEPARATOR = "";
3328
+ var CONTENT_ROOT_PREFIX = "~";
3329
+ var SUBLEVEL_OPTIONS = {
3330
+ separator: INDEX_KEY_FIELD_SEPARATOR,
3331
+ valueEncoding: "json"
3119
3332
  };
3120
- var TinaFetchError = class extends Error {
3121
- constructor(message, args) {
3122
- super(message);
3123
- this.name = "TinaFetchError";
3124
- this.collection = args.collection;
3125
- this.stack = args.stack;
3126
- this.file = args.file;
3127
- this.originalError = args.originalError;
3333
+ var LevelProxyHandler = {
3334
+ get: function(target, property) {
3335
+ if (!target[property]) {
3336
+ throw new Error(`The property, ${property.toString()}, doesn't exist`);
3337
+ }
3338
+ if (typeof target[property] !== "function") {
3339
+ throw new Error(
3340
+ `The property, ${property.toString()}, is not a function`
3341
+ );
3342
+ }
3343
+ if (property === "get") {
3344
+ return async (...args) => {
3345
+ let result;
3346
+ try {
3347
+ result = await target[property].apply(target, args);
3348
+ } catch (e) {
3349
+ if (e.code !== "LEVEL_NOT_FOUND") {
3350
+ throw e;
3351
+ }
3352
+ }
3353
+ return result;
3354
+ };
3355
+ } else if (property === "sublevel") {
3356
+ return (...args) => {
3357
+ return new Proxy(
3358
+ // eslint-disable-next-line prefer-spread
3359
+ target[property].apply(target, args),
3360
+ LevelProxyHandler
3361
+ );
3362
+ };
3363
+ } else {
3364
+ return (...args) => target[property].apply(target, args);
3365
+ }
3128
3366
  }
3129
3367
  };
3130
- var TinaQueryError = class extends TinaFetchError {
3131
- constructor(args) {
3132
- super(
3133
- `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3134
- args
3135
- );
3368
+ var LevelProxy = class {
3369
+ constructor(level) {
3370
+ return new Proxy(level, LevelProxyHandler);
3136
3371
  }
3137
3372
  };
3138
- var TinaParseDocumentError = class extends TinaFetchError {
3139
- constructor(args) {
3140
- super(
3141
- `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3142
- args
3373
+
3374
+ // src/database/datalayer.ts
3375
+ import path2 from "path";
3376
+
3377
+ // src/database/util.ts
3378
+ import toml from "@iarna/toml";
3379
+ import {
3380
+ normalizePath
3381
+ } from "@tinacms/schema-tools";
3382
+ import matter from "gray-matter";
3383
+ import yaml from "js-yaml";
3384
+ import path from "path";
3385
+ import micromatch from "micromatch";
3386
+
3387
+ // src/database/alias-utils.ts
3388
+ var replaceBlockAliases = (template, item) => {
3389
+ const output = { ...item };
3390
+ const templateKey = template.templateKey || "_template";
3391
+ const templateName = output[templateKey];
3392
+ const matchingTemplate = template.templates.find(
3393
+ (t) => t.nameOverride == templateName || t.name == templateName
3394
+ );
3395
+ if (!matchingTemplate) {
3396
+ throw new Error(
3397
+ `Block template "${templateName}" is not defined for field "${template.name}"`
3143
3398
  );
3144
3399
  }
3145
- toString() {
3146
- return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
3400
+ output._template = matchingTemplate.name;
3401
+ if (templateKey != "_template") {
3402
+ delete output[templateKey];
3147
3403
  }
3404
+ return output;
3148
3405
  };
3149
- var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
3150
- var handleFetchErrorError = (e, verbose) => {
3151
- if (e instanceof Error) {
3152
- if (e instanceof TinaFetchError) {
3153
- if (verbose) {
3154
- console.log(e.toString());
3155
- console.log(e);
3156
- console.log(e.stack);
3406
+ var replaceNameOverrides = (template, obj) => {
3407
+ if (template.list) {
3408
+ return obj.map((item) => {
3409
+ if (isBlockField(template)) {
3410
+ item = replaceBlockAliases(template, item);
3157
3411
  }
3158
- }
3412
+ return _replaceNameOverrides(
3413
+ getTemplateForData(template, item).fields,
3414
+ item
3415
+ );
3416
+ });
3159
3417
  } else {
3160
- console.error(e);
3418
+ return _replaceNameOverrides(getTemplateForData(template, obj).fields, obj);
3161
3419
  }
3162
- throw e;
3163
3420
  };
3164
-
3165
- // src/resolver/filter-utils.ts
3166
- var resolveReferences = async (filter, fields, resolver) => {
3167
- for (const fieldKey of Object.keys(filter)) {
3168
- const fieldDefinition = fields.find(
3169
- (f) => f.name === fieldKey
3421
+ function isBlockField(field) {
3422
+ return field && field.type === "object" && field.templates?.length > 0;
3423
+ }
3424
+ var _replaceNameOverrides = (fields, obj) => {
3425
+ const output = {};
3426
+ Object.keys(obj).forEach((key) => {
3427
+ const field = fields.find(
3428
+ (fieldWithMatchingAlias) => (fieldWithMatchingAlias?.nameOverride || fieldWithMatchingAlias?.name) === key
3170
3429
  );
3171
- if (fieldDefinition) {
3172
- if (fieldDefinition.type === "reference") {
3173
- const { edges, values } = await resolver(filter, fieldDefinition);
3174
- if (edges.length === 1) {
3175
- filter[fieldKey] = {
3176
- eq: values[0]
3177
- };
3178
- } else if (edges.length > 1) {
3179
- filter[fieldKey] = {
3180
- in: values
3181
- };
3182
- } else {
3183
- filter[fieldKey] = {
3184
- eq: "___null___"
3185
- };
3186
- }
3187
- } else if (fieldDefinition.type === "object") {
3188
- if (fieldDefinition.templates) {
3189
- for (const templateName of Object.keys(filter[fieldKey])) {
3190
- const template = fieldDefinition.templates.find(
3191
- (template2) => !(typeof template2 === "string") && template2.name === templateName
3192
- );
3193
- if (template) {
3194
- await resolveReferences(
3195
- filter[fieldKey][templateName],
3196
- template.fields,
3197
- resolver
3198
- );
3199
- } else {
3200
- throw new Error(`Template ${templateName} not found`);
3201
- }
3202
- }
3203
- } else {
3204
- await resolveReferences(
3205
- filter[fieldKey],
3206
- fieldDefinition.fields,
3207
- resolver
3208
- );
3209
- }
3430
+ output[field?.name || key] = field?.type == "object" ? replaceNameOverrides(field, obj[key]) : obj[key];
3431
+ });
3432
+ return output;
3433
+ };
3434
+ var getTemplateForData = (field, data) => {
3435
+ if (field.templates?.length) {
3436
+ const templateKey = "_template";
3437
+ if (data[templateKey]) {
3438
+ const result = field.templates.find(
3439
+ (template) => template.nameOverride === data[templateKey] || template.name === data[templateKey]
3440
+ );
3441
+ if (result) {
3442
+ return result;
3210
3443
  }
3211
- } else {
3212
- throw new Error(`Unable to find field ${fieldKey}`);
3444
+ throw new Error(
3445
+ `Template "${data[templateKey]}" is not defined for field "${field.name}"`
3446
+ );
3213
3447
  }
3448
+ throw new Error(
3449
+ `Missing required key "${templateKey}" on field "${field.name}"`
3450
+ );
3451
+ } else {
3452
+ return field;
3214
3453
  }
3215
3454
  };
3216
- var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
3217
- for (const childFieldName of Object.keys(filterNode)) {
3218
- const childField = fields.find((field) => field.name === childFieldName);
3219
- if (!childField) {
3220
- throw new Error(`Unable to find type for field ${childFieldName}`);
3221
- }
3222
- collectConditionsForField(
3223
- childFieldName,
3224
- childField,
3225
- filterNode[childFieldName],
3226
- pathExpression,
3227
- collectCondition
3228
- );
3229
- }
3230
- };
3231
- var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3232
- if (field.list && field.templates) {
3233
- for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
3234
- const template = field.templates.find(
3235
- (template2) => !(typeof template2 === "string") && template2.name === filterKey
3236
- );
3237
- const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
3238
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
3239
- collectConditionsForChildFields(
3240
- childFilterNode,
3241
- template.fields,
3242
- filterPath,
3243
- collectCondition
3244
- );
3245
- }
3246
- } else {
3247
- const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
3248
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
3249
- collectConditionsForChildFields(
3250
- filterNode,
3251
- field.fields,
3252
- filterPath,
3253
- collectCondition
3254
- );
3255
- }
3256
- };
3257
- var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3258
- if (field.type === "object") {
3259
- collectConditionsForObjectField(
3260
- fieldName,
3261
- field,
3262
- filterNode,
3263
- pathExpression,
3264
- collectCondition
3265
- );
3266
- } else {
3267
- collectCondition({
3268
- filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
3269
- filterExpression: {
3270
- _type: field.type,
3271
- _list: !!field.list,
3272
- ...filterNode
3273
- }
3274
- });
3275
- }
3276
- };
3277
-
3278
- // src/resolver/media-utils.ts
3279
- var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
3280
- if (config && value) {
3281
- if (config.useRelativeMedia === true) {
3282
- return value;
3283
- }
3284
- if (hasTinaMediaConfig(schema) === true) {
3285
- const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
3286
- if (typeof value === "string" && value.includes(assetsURL)) {
3287
- const cleanMediaRoot = cleanUpSlashes(
3288
- schema.config.media.tina.mediaRoot
3289
- );
3290
- const strippedURL = value.replace(assetsURL, "");
3291
- return `${cleanMediaRoot}${strippedURL}`;
3292
- }
3293
- if (Array.isArray(value)) {
3294
- return value.map((v) => {
3295
- if (!v || typeof v !== "string")
3296
- return v;
3297
- const cleanMediaRoot = cleanUpSlashes(
3298
- schema.config.media.tina.mediaRoot
3299
- );
3300
- const strippedURL = v.replace(assetsURL, "");
3301
- return `${cleanMediaRoot}${strippedURL}`;
3302
- });
3303
- }
3304
- return value;
3305
- }
3306
- return value;
3307
- } else {
3308
- return value;
3309
- }
3310
- };
3311
- var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
3312
- if (config && value) {
3313
- if (config.useRelativeMedia === true) {
3314
- return value;
3315
- }
3316
- if (hasTinaMediaConfig(schema) === true) {
3317
- const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
3318
- if (typeof value === "string") {
3319
- const strippedValue = value.replace(cleanMediaRoot, "");
3320
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3321
- }
3322
- if (Array.isArray(value)) {
3323
- return value.map((v) => {
3324
- if (!v || typeof v !== "string")
3325
- return v;
3326
- const strippedValue = v.replace(cleanMediaRoot, "");
3327
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3328
- });
3329
- }
3330
- }
3331
- return value;
3332
- } else {
3333
- return value;
3334
- }
3335
- };
3336
- var cleanUpSlashes = (path7) => {
3337
- if (path7) {
3338
- return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
3339
- }
3340
- return "";
3341
- };
3342
- var hasTinaMediaConfig = (schema) => {
3343
- if (!schema.config?.media?.tina)
3344
- return false;
3345
- if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
3346
- return false;
3347
- return true;
3348
- };
3349
-
3350
- // src/resolver/index.ts
3351
- import { GraphQLError as GraphQLError2 } from "graphql";
3352
-
3353
- // src/database/datalayer.ts
3354
- import { JSONPath } from "jsonpath-plus";
3355
- import sha from "js-sha1";
3356
-
3357
- // src/database/level.ts
3358
- var ARRAY_ITEM_VALUE_SEPARATOR = ",";
3359
- var INDEX_KEY_FIELD_SEPARATOR = "";
3360
- var CONTENT_ROOT_PREFIX = "~";
3361
- var SUBLEVEL_OPTIONS = {
3362
- separator: INDEX_KEY_FIELD_SEPARATOR,
3363
- valueEncoding: "json"
3364
- };
3365
- var LevelProxyHandler = {
3366
- get: function(target, property) {
3367
- if (!target[property]) {
3368
- throw new Error(`The property, ${property.toString()}, doesn't exist`);
3369
- }
3370
- if (typeof target[property] !== "function") {
3371
- throw new Error(`The property, ${property.toString()}, is not a function`);
3372
- }
3373
- if (property === "get") {
3374
- return async (...args) => {
3375
- let result;
3376
- try {
3377
- result = await target[property].apply(target, args);
3378
- } catch (e) {
3379
- if (e.code !== "LEVEL_NOT_FOUND") {
3380
- throw e;
3381
- }
3382
- }
3383
- return result;
3384
- };
3385
- } else if (property === "sublevel") {
3386
- return (...args) => {
3387
- return new Proxy(
3388
- target[property].apply(target, args),
3389
- LevelProxyHandler
3390
- );
3391
- };
3392
- } else {
3393
- return (...args) => target[property].apply(target, args);
3394
- }
3395
- }
3396
- };
3397
- var LevelProxy = class {
3398
- constructor(level) {
3399
- return new Proxy(level, LevelProxyHandler);
3400
- }
3401
- };
3402
-
3403
- // src/database/datalayer.ts
3404
- import path2 from "path";
3405
-
3406
- // src/database/util.ts
3407
- import toml from "@iarna/toml";
3408
- import yaml from "js-yaml";
3409
- import matter from "gray-matter";
3410
- import {
3411
- normalizePath
3412
- } from "@tinacms/schema-tools";
3413
- import micromatch from "micromatch";
3414
- import path from "path";
3415
-
3416
- // src/database/alias-utils.ts
3417
- var replaceBlockAliases = (template, item) => {
3418
- const output = { ...item };
3419
- const templateKey = template.templateKey || "_template";
3420
- const templateName = output[templateKey];
3421
- const matchingTemplate = template.templates.find(
3422
- (t) => t.nameOverride == templateName || t.name == templateName
3423
- );
3424
- if (!matchingTemplate) {
3425
- throw new Error(
3426
- `Block template "${templateName}" is not defined for field "${template.name}"`
3427
- );
3428
- }
3429
- output._template = matchingTemplate.name;
3430
- if (templateKey != "_template") {
3431
- delete output[templateKey];
3432
- }
3433
- return output;
3434
- };
3435
- var replaceNameOverrides = (template, obj) => {
3436
- if (template.list) {
3437
- return obj.map((item) => {
3438
- if (isBlockField(template)) {
3439
- item = replaceBlockAliases(template, item);
3440
- }
3441
- return _replaceNameOverrides(
3442
- getTemplateForData(template, item).fields,
3443
- item
3444
- );
3445
- });
3446
- } else {
3447
- return _replaceNameOverrides(getTemplateForData(template, obj).fields, obj);
3448
- }
3449
- };
3450
- function isBlockField(field) {
3451
- return field && field.type === "object" && field.templates?.length > 0;
3452
- }
3453
- var _replaceNameOverrides = (fields, obj) => {
3454
- const output = {};
3455
- Object.keys(obj).forEach((key) => {
3456
- const field = fields.find(
3457
- (fieldWithMatchingAlias) => (fieldWithMatchingAlias?.nameOverride || fieldWithMatchingAlias?.name) === key
3458
- );
3459
- output[field?.name || key] = field?.type == "object" ? replaceNameOverrides(field, obj[key]) : obj[key];
3460
- });
3461
- return output;
3462
- };
3463
- var getTemplateForData = (field, data) => {
3464
- if (field.templates?.length) {
3465
- const templateKey = "_template";
3466
- if (data[templateKey]) {
3467
- const result = field.templates.find(
3468
- (template) => template.nameOverride === data[templateKey] || template.name === data[templateKey]
3469
- );
3470
- if (result) {
3471
- return result;
3472
- }
3473
- throw new Error(
3474
- `Template "${data[templateKey]}" is not defined for field "${field.name}"`
3475
- );
3476
- }
3477
- throw new Error(
3478
- `Missing required key "${templateKey}" on field "${field.name}"`
3479
- );
3480
- } else {
3481
- return field;
3482
- }
3483
- };
3484
- var applyBlockAliases = (template, item) => {
3485
- const output = { ...item };
3486
- const templateKey = template.templateKey || "_template";
3487
- const templateName = output._template;
3488
- const matchingTemplate = template.templates.find(
3489
- (t) => t.nameOverride == templateName || t.name == templateName
3490
- );
3491
- if (!matchingTemplate) {
3492
- throw new Error(
3493
- `Block template "${templateName}" is not defined for field "${template.name}"`
3455
+ var applyBlockAliases = (template, item) => {
3456
+ const output = { ...item };
3457
+ const templateKey = template.templateKey || "_template";
3458
+ const templateName = output._template;
3459
+ const matchingTemplate = template.templates.find(
3460
+ (t) => t.nameOverride == templateName || t.name == templateName
3461
+ );
3462
+ if (!matchingTemplate) {
3463
+ throw new Error(
3464
+ `Block template "${templateName}" is not defined for field "${template.name}"`
3494
3465
  );
3495
3466
  }
3496
3467
  output[templateKey] = matchingTemplate.nameOverride || matchingTemplate.name;
@@ -3752,6 +3723,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3752
3723
 
3753
3724
  // src/database/datalayer.ts
3754
3725
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
3726
+ var REFS_COLLECTIONS_SORT_KEY = "__refs__";
3727
+ var REFS_REFERENCE_FIELD = "__tina_ref__";
3728
+ var REFS_PATH_FIELD = "__tina_ref_path__";
3755
3729
  var DEFAULT_NUMERIC_LPAD = 4;
3756
3730
  var applyPadding = (input, pad) => {
3757
3731
  if (pad) {
@@ -4261,6 +4235,7 @@ var makeFolderOpsForCollection = (folderTree, collection, indexDefinitions, opTy
4261
4235
  result.push({
4262
4236
  type: opType,
4263
4237
  key: `${collection.path}/${subFolderKey}.${collection.format}`,
4238
+ // replace the root with the collection path
4264
4239
  sublevel: indexSublevel,
4265
4240
  value: {}
4266
4241
  });
@@ -4324,6 +4299,57 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4324
4299
  }
4325
4300
  return result;
4326
4301
  };
4302
+ var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4303
+ const result = [];
4304
+ if (collection) {
4305
+ for (const [c, referencePaths] of Object.entries(references || {})) {
4306
+ if (!referencePaths.length) {
4307
+ continue;
4308
+ }
4309
+ const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4310
+ const refSublevel = collectionSublevel.sublevel(
4311
+ REFS_COLLECTIONS_SORT_KEY,
4312
+ SUBLEVEL_OPTIONS
4313
+ );
4314
+ const references2 = {};
4315
+ for (const path7 of referencePaths) {
4316
+ const ref = JSONPath({ path: path7, json: data });
4317
+ if (!ref) {
4318
+ continue;
4319
+ }
4320
+ if (Array.isArray(ref)) {
4321
+ for (const r of ref) {
4322
+ if (!r) {
4323
+ continue;
4324
+ }
4325
+ if (references2[r]) {
4326
+ references2[r].push(path7);
4327
+ } else {
4328
+ references2[r] = [path7];
4329
+ }
4330
+ }
4331
+ } else {
4332
+ if (references2[ref]) {
4333
+ references2[ref].push(path7);
4334
+ } else {
4335
+ references2[ref] = [path7];
4336
+ }
4337
+ }
4338
+ }
4339
+ for (const ref of Object.keys(references2)) {
4340
+ for (const path7 of references2[ref]) {
4341
+ result.push({
4342
+ type: opType,
4343
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4344
+ sublevel: refSublevel,
4345
+ value: opType === "put" ? {} : void 0
4346
+ });
4347
+ }
4348
+ }
4349
+ }
4350
+ }
4351
+ return result;
4352
+ };
4327
4353
  var makeStringEscaper = (regex, replacement) => {
4328
4354
  return (input) => {
4329
4355
  if (Array.isArray(input)) {
@@ -4339,10 +4365,246 @@ var makeStringEscaper = (regex, replacement) => {
4339
4365
  }
4340
4366
  };
4341
4367
  };
4342
- var stringEscaper = makeStringEscaper(
4343
- new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4344
- encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4345
- );
4368
+ var stringEscaper = makeStringEscaper(
4369
+ new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4370
+ encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4371
+ );
4372
+
4373
+ // src/resolver/error.ts
4374
+ var TinaGraphQLError = class extends Error {
4375
+ constructor(message, extensions) {
4376
+ super(message);
4377
+ if (!this.name) {
4378
+ Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
4379
+ }
4380
+ this.extensions = { ...extensions };
4381
+ }
4382
+ };
4383
+ var TinaFetchError = class extends Error {
4384
+ constructor(message, args) {
4385
+ super(message);
4386
+ this.name = "TinaFetchError";
4387
+ this.collection = args.collection;
4388
+ this.file = args.file;
4389
+ this.originalError = args.originalError;
4390
+ }
4391
+ };
4392
+ var TinaQueryError = class extends TinaFetchError {
4393
+ constructor(args) {
4394
+ super(
4395
+ `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4396
+ args
4397
+ );
4398
+ }
4399
+ };
4400
+ var TinaParseDocumentError = class extends TinaFetchError {
4401
+ constructor(args) {
4402
+ super(
4403
+ `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4404
+ args
4405
+ );
4406
+ }
4407
+ toString() {
4408
+ return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
4409
+ }
4410
+ };
4411
+ var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
4412
+ var handleFetchErrorError = (e, verbose) => {
4413
+ if (e instanceof Error) {
4414
+ if (e instanceof TinaFetchError) {
4415
+ if (verbose) {
4416
+ console.log(e.toString());
4417
+ console.log(e);
4418
+ console.log(e.stack);
4419
+ }
4420
+ }
4421
+ } else {
4422
+ console.error(e);
4423
+ }
4424
+ throw e;
4425
+ };
4426
+
4427
+ // src/resolver/filter-utils.ts
4428
+ var resolveReferences = async (filter, fields, resolver) => {
4429
+ for (const fieldKey of Object.keys(filter)) {
4430
+ const fieldDefinition = fields.find(
4431
+ (f) => f.name === fieldKey
4432
+ );
4433
+ if (fieldDefinition) {
4434
+ if (fieldDefinition.type === "reference") {
4435
+ const { edges, values } = await resolver(filter, fieldDefinition);
4436
+ if (edges.length === 1) {
4437
+ filter[fieldKey] = {
4438
+ eq: values[0]
4439
+ };
4440
+ } else if (edges.length > 1) {
4441
+ filter[fieldKey] = {
4442
+ in: values
4443
+ };
4444
+ } else {
4445
+ filter[fieldKey] = {
4446
+ eq: "___null___"
4447
+ };
4448
+ }
4449
+ } else if (fieldDefinition.type === "object") {
4450
+ if (fieldDefinition.templates) {
4451
+ for (const templateName of Object.keys(filter[fieldKey])) {
4452
+ const template = fieldDefinition.templates.find(
4453
+ (template2) => !(typeof template2 === "string") && template2.name === templateName
4454
+ );
4455
+ if (template) {
4456
+ await resolveReferences(
4457
+ filter[fieldKey][templateName],
4458
+ template.fields,
4459
+ resolver
4460
+ );
4461
+ } else {
4462
+ throw new Error(`Template ${templateName} not found`);
4463
+ }
4464
+ }
4465
+ } else {
4466
+ await resolveReferences(
4467
+ filter[fieldKey],
4468
+ fieldDefinition.fields,
4469
+ resolver
4470
+ );
4471
+ }
4472
+ }
4473
+ } else {
4474
+ throw new Error(`Unable to find field ${fieldKey}`);
4475
+ }
4476
+ }
4477
+ };
4478
+ var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
4479
+ for (const childFieldName of Object.keys(filterNode)) {
4480
+ const childField = fields.find((field) => field.name === childFieldName);
4481
+ if (!childField) {
4482
+ throw new Error(`Unable to find type for field ${childFieldName}`);
4483
+ }
4484
+ collectConditionsForField(
4485
+ childFieldName,
4486
+ childField,
4487
+ filterNode[childFieldName],
4488
+ pathExpression,
4489
+ collectCondition
4490
+ );
4491
+ }
4492
+ };
4493
+ var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4494
+ if (field.list && field.templates) {
4495
+ for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
4496
+ const template = field.templates.find(
4497
+ (template2) => !(typeof template2 === "string") && template2.name === filterKey
4498
+ );
4499
+ const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
4500
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
4501
+ collectConditionsForChildFields(
4502
+ childFilterNode,
4503
+ template.fields,
4504
+ filterPath,
4505
+ collectCondition
4506
+ );
4507
+ }
4508
+ } else {
4509
+ const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
4510
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
4511
+ collectConditionsForChildFields(
4512
+ filterNode,
4513
+ field.fields,
4514
+ filterPath,
4515
+ collectCondition
4516
+ );
4517
+ }
4518
+ };
4519
+ var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4520
+ if (field.type === "object") {
4521
+ collectConditionsForObjectField(
4522
+ fieldName,
4523
+ field,
4524
+ filterNode,
4525
+ pathExpression,
4526
+ collectCondition
4527
+ );
4528
+ } else {
4529
+ collectCondition({
4530
+ filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
4531
+ filterExpression: {
4532
+ _type: field.type,
4533
+ _list: !!field.list,
4534
+ ...filterNode
4535
+ }
4536
+ });
4537
+ }
4538
+ };
4539
+
4540
+ // src/resolver/media-utils.ts
4541
+ var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
4542
+ if (config && value) {
4543
+ if (config.useRelativeMedia === true) {
4544
+ return value;
4545
+ }
4546
+ if (hasTinaMediaConfig(schema) === true) {
4547
+ const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
4548
+ if (typeof value === "string" && value.includes(assetsURL)) {
4549
+ const cleanMediaRoot = cleanUpSlashes(
4550
+ schema.config.media.tina.mediaRoot
4551
+ );
4552
+ const strippedURL = value.replace(assetsURL, "");
4553
+ return `${cleanMediaRoot}${strippedURL}`;
4554
+ }
4555
+ if (Array.isArray(value)) {
4556
+ return value.map((v) => {
4557
+ if (!v || typeof v !== "string") return v;
4558
+ const cleanMediaRoot = cleanUpSlashes(
4559
+ schema.config.media.tina.mediaRoot
4560
+ );
4561
+ const strippedURL = v.replace(assetsURL, "");
4562
+ return `${cleanMediaRoot}${strippedURL}`;
4563
+ });
4564
+ }
4565
+ return value;
4566
+ }
4567
+ return value;
4568
+ } else {
4569
+ return value;
4570
+ }
4571
+ };
4572
+ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
4573
+ if (config && value) {
4574
+ if (config.useRelativeMedia === true) {
4575
+ return value;
4576
+ }
4577
+ if (hasTinaMediaConfig(schema) === true) {
4578
+ const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
4579
+ if (typeof value === "string") {
4580
+ const strippedValue = value.replace(cleanMediaRoot, "");
4581
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4582
+ }
4583
+ if (Array.isArray(value)) {
4584
+ return value.map((v) => {
4585
+ if (!v || typeof v !== "string") return v;
4586
+ const strippedValue = v.replace(cleanMediaRoot, "");
4587
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4588
+ });
4589
+ }
4590
+ }
4591
+ return value;
4592
+ } else {
4593
+ return value;
4594
+ }
4595
+ };
4596
+ var cleanUpSlashes = (path7) => {
4597
+ if (path7) {
4598
+ return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
4599
+ }
4600
+ return "";
4601
+ };
4602
+ var hasTinaMediaConfig = (schema) => {
4603
+ if (!schema.config?.media?.tina) return false;
4604
+ if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
4605
+ return false;
4606
+ return true;
4607
+ };
4346
4608
 
4347
4609
  // src/resolver/index.ts
4348
4610
  var createResolver = (args) => {
@@ -4375,6 +4637,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4375
4637
  case "password":
4376
4638
  accumulator[field.name] = {
4377
4639
  value: void 0,
4640
+ // never resolve the password hash
4378
4641
  passwordChangeRequired: value["passwordChangeRequired"] ?? false
4379
4642
  };
4380
4643
  break;
@@ -4469,7 +4732,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4469
4732
  }
4470
4733
  return accumulator;
4471
4734
  };
4472
- var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit) => {
4735
+ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit, hasReferences) => {
4473
4736
  const collection = tinaSchema.getCollection(rawData._collection);
4474
4737
  try {
4475
4738
  const template = tinaSchema.getTemplateForData({
@@ -4503,8 +4766,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4503
4766
  originalError: e,
4504
4767
  collection: collection.name,
4505
4768
  includeAuditMessage: !isAudit,
4506
- file: relativePath,
4507
- stack: e.stack
4769
+ file: relativePath
4508
4770
  });
4509
4771
  }
4510
4772
  const titleField = template.fields.find((x) => {
@@ -4523,6 +4785,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4523
4785
  basename,
4524
4786
  filename,
4525
4787
  extension,
4788
+ hasReferences,
4526
4789
  path: fullPath,
4527
4790
  relativePath,
4528
4791
  breadcrumbs,
@@ -4542,6 +4805,34 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4542
4805
  throw e;
4543
4806
  }
4544
4807
  };
4808
+ var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4809
+ let updated = false;
4810
+ if (!path7.includes(".") && !path7.includes("[")) {
4811
+ if (path7 in obj && obj[path7] === oldValue) {
4812
+ obj[path7] = newValue;
4813
+ updated = true;
4814
+ }
4815
+ return { object: obj, updated };
4816
+ }
4817
+ const parentPath = path7.replace(/\.[^.\[\]]+$/, "");
4818
+ const keyToUpdate = path7.match(/[^.\[\]]+$/)[0];
4819
+ const parents = JSONPath2({
4820
+ path: parentPath,
4821
+ json: obj,
4822
+ resultType: "value"
4823
+ });
4824
+ if (parents.length > 0) {
4825
+ parents.forEach((parent) => {
4826
+ if (parent && typeof parent === "object" && keyToUpdate in parent) {
4827
+ if (parent[keyToUpdate] === oldValue) {
4828
+ parent[keyToUpdate] = newValue;
4829
+ updated = true;
4830
+ }
4831
+ }
4832
+ });
4833
+ }
4834
+ return { object: obj, updated };
4835
+ };
4545
4836
  var Resolver = class {
4546
4837
  constructor(init) {
4547
4838
  this.init = init;
@@ -4549,6 +4840,7 @@ var Resolver = class {
4549
4840
  const collection = this.tinaSchema.getCollection(collectionName);
4550
4841
  const extraFields = {};
4551
4842
  return {
4843
+ // return the collection and hasDocuments to resolve documents at a lower level
4552
4844
  documents: { collection, hasDocuments },
4553
4845
  ...collection,
4554
4846
  ...extraFields
@@ -4556,7 +4848,9 @@ var Resolver = class {
4556
4848
  };
4557
4849
  this.getRaw = async (fullPath) => {
4558
4850
  if (typeof fullPath !== "string") {
4559
- throw new Error(`fullPath must be of type string for getDocument request`);
4851
+ throw new Error(
4852
+ `fullPath must be of type string for getDocument request`
4853
+ );
4560
4854
  }
4561
4855
  return this.database.get(fullPath);
4562
4856
  };
@@ -4583,22 +4877,28 @@ var Resolver = class {
4583
4877
  );
4584
4878
  }
4585
4879
  };
4586
- this.getDocument = async (fullPath) => {
4880
+ this.getDocument = async (fullPath, opts = {}) => {
4587
4881
  if (typeof fullPath !== "string") {
4588
- throw new Error(`fullPath must be of type string for getDocument request`);
4882
+ throw new Error(
4883
+ `fullPath must be of type string for getDocument request`
4884
+ );
4589
4885
  }
4590
4886
  const rawData = await this.getRaw(fullPath);
4887
+ const hasReferences = opts?.checkReferences ? await this.hasReferences(fullPath, opts.collection) : void 0;
4591
4888
  return transformDocumentIntoPayload(
4592
4889
  fullPath,
4593
4890
  rawData,
4594
4891
  this.tinaSchema,
4595
4892
  this.config,
4596
- this.isAudit
4893
+ this.isAudit,
4894
+ hasReferences
4597
4895
  );
4598
4896
  };
4599
4897
  this.deleteDocument = async (fullPath) => {
4600
4898
  if (typeof fullPath !== "string") {
4601
- throw new Error(`fullPath must be of type string for getDocument request`);
4899
+ throw new Error(
4900
+ `fullPath must be of type string for getDocument request`
4901
+ );
4602
4902
  }
4603
4903
  await this.database.delete(fullPath);
4604
4904
  };
@@ -4633,7 +4933,9 @@ var Resolver = class {
4633
4933
  );
4634
4934
  } else {
4635
4935
  return this.buildFieldMutations(
4936
+ // @ts-ignore FIXME Argument of type 'string | object' is not assignable to parameter of type '{ [fieldName: string]: string | object | (string | object)[]; }'
4636
4937
  fieldValue,
4938
+ //@ts-ignore
4637
4939
  objectTemplate,
4638
4940
  existingData
4639
4941
  );
@@ -4645,6 +4947,7 @@ var Resolver = class {
4645
4947
  fieldValue.map(async (item) => {
4646
4948
  if (typeof item === "string") {
4647
4949
  throw new Error(
4950
+ //@ts-ignore
4648
4951
  `Expected object for template value for field ${field.name}`
4649
4952
  );
4650
4953
  }
@@ -4653,16 +4956,19 @@ var Resolver = class {
4653
4956
  });
4654
4957
  const [templateName] = Object.entries(item)[0];
4655
4958
  const template = templates.find(
4959
+ //@ts-ignore
4656
4960
  (template2) => template2.name === templateName
4657
4961
  );
4658
4962
  if (!template) {
4659
4963
  throw new Error(`Expected to find template ${templateName}`);
4660
4964
  }
4661
4965
  return {
4966
+ // @ts-ignore FIXME Argument of type 'unknown' is not assignable to parameter of type '{ [fieldName: string]: string | { [key: string]: unknown; } | (string | { [key: string]: unknown; })[]; }'
4662
4967
  ...await this.buildFieldMutations(
4663
4968
  item[template.name],
4664
4969
  template
4665
4970
  ),
4971
+ //@ts-ignore
4666
4972
  _template: template.name
4667
4973
  };
4668
4974
  })
@@ -4670,6 +4976,7 @@ var Resolver = class {
4670
4976
  } else {
4671
4977
  if (typeof fieldValue === "string") {
4672
4978
  throw new Error(
4979
+ //@ts-ignore
4673
4980
  `Expected object for template value for field ${field.name}`
4674
4981
  );
4675
4982
  }
@@ -4678,16 +4985,19 @@ var Resolver = class {
4678
4985
  });
4679
4986
  const [templateName] = Object.entries(fieldValue)[0];
4680
4987
  const template = templates.find(
4988
+ //@ts-ignore
4681
4989
  (template2) => template2.name === templateName
4682
4990
  );
4683
4991
  if (!template) {
4684
4992
  throw new Error(`Expected to find template ${templateName}`);
4685
4993
  }
4686
4994
  return {
4995
+ // @ts-ignore FIXME Argument of type 'unknown' is not assignable to parameter of type '{ [fieldName: string]: string | { [key: string]: unknown; } | (string | { [key: string]: unknown; })[]; }'
4687
4996
  ...await this.buildFieldMutations(
4688
4997
  fieldValue[template.name],
4689
4998
  template
4690
4999
  ),
5000
+ //@ts-ignore
4691
5001
  _template: template.name
4692
5002
  };
4693
5003
  }
@@ -4727,6 +5037,7 @@ var Resolver = class {
4727
5037
  return this.getDocument(realPath);
4728
5038
  }
4729
5039
  const params = await this.buildObjectMutations(
5040
+ // @ts-ignore
4730
5041
  args.params[collection.name],
4731
5042
  collection
4732
5043
  );
@@ -4772,6 +5083,7 @@ var Resolver = class {
4772
5083
  const values = {
4773
5084
  ...oldDoc,
4774
5085
  ...await this.buildFieldMutations(
5086
+ // @ts-ignore FIXME: failing on unknown, which we don't need to know because it's recursive
4775
5087
  templateParams,
4776
5088
  template,
4777
5089
  doc?._rawData
@@ -4785,13 +5097,22 @@ var Resolver = class {
4785
5097
  return this.getDocument(realPath);
4786
5098
  }
4787
5099
  const params = await this.buildObjectMutations(
5100
+ //@ts-ignore
4788
5101
  isCollectionSpecific ? args.params : args.params[collection.name],
4789
5102
  collection,
4790
5103
  doc?._rawData
4791
5104
  );
4792
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
5105
+ await this.database.put(
5106
+ realPath,
5107
+ { ...oldDoc, ...params },
5108
+ collection.name
5109
+ );
4793
5110
  return this.getDocument(realPath);
4794
5111
  };
5112
+ /**
5113
+ * Returns top-level fields which are not defined in the collection, so their
5114
+ * values are not eliminated from Tina when new values are saved
5115
+ */
4795
5116
  this.resolveLegacyValues = (oldDoc, collection) => {
4796
5117
  const legacyValues = {};
4797
5118
  Object.entries(oldDoc).forEach(([key, value]) => {
@@ -4896,6 +5217,40 @@ var Resolver = class {
4896
5217
  if (isDeletion) {
4897
5218
  const doc = await this.getDocument(realPath);
4898
5219
  await this.deleteDocument(realPath);
5220
+ if (await this.hasReferences(realPath, collection)) {
5221
+ const collRefs = await this.findReferences(realPath, collection);
5222
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5223
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5224
+ docsWithRefs
5225
+ )) {
5226
+ let refDoc = await this.getRaw(pathToDocWithRef);
5227
+ let hasUpdate = false;
5228
+ for (const path7 of referencePaths) {
5229
+ const { object: object2, updated } = updateObjectWithJsonPath(
5230
+ refDoc,
5231
+ path7,
5232
+ realPath,
5233
+ null
5234
+ );
5235
+ refDoc = object2;
5236
+ hasUpdate = updated || hasUpdate;
5237
+ }
5238
+ if (hasUpdate) {
5239
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5240
+ if (!collectionWithRef) {
5241
+ throw new Error(
5242
+ `Unable to find collection for ${pathToDocWithRef}`
5243
+ );
5244
+ }
5245
+ await this.database.put(
5246
+ pathToDocWithRef,
5247
+ refDoc,
5248
+ collectionWithRef.name
5249
+ );
5250
+ }
5251
+ }
5252
+ }
5253
+ }
4899
5254
  return doc;
4900
5255
  }
4901
5256
  if (isUpdateName) {
@@ -4912,12 +5267,49 @@ var Resolver = class {
4912
5267
  collection?.path,
4913
5268
  args.params.relativePath
4914
5269
  );
5270
+ if (newRealPath === realPath) {
5271
+ return doc;
5272
+ }
4915
5273
  await this.database.put(newRealPath, doc._rawData, collection.name);
4916
5274
  await this.deleteDocument(realPath);
5275
+ const collRefs = await this.findReferences(realPath, collection);
5276
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5277
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5278
+ docsWithRefs
5279
+ )) {
5280
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5281
+ let hasUpdate = false;
5282
+ for (const path7 of referencePaths) {
5283
+ const { object: object2, updated } = updateObjectWithJsonPath(
5284
+ docWithRef,
5285
+ path7,
5286
+ realPath,
5287
+ newRealPath
5288
+ );
5289
+ docWithRef = object2;
5290
+ hasUpdate = updated || hasUpdate;
5291
+ }
5292
+ if (hasUpdate) {
5293
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5294
+ if (!collectionWithRef) {
5295
+ throw new Error(
5296
+ `Unable to find collection for ${pathToDocWithRef}`
5297
+ );
5298
+ }
5299
+ await this.database.put(
5300
+ pathToDocWithRef,
5301
+ docWithRef,
5302
+ collectionWithRef.name
5303
+ );
5304
+ }
5305
+ }
5306
+ }
4917
5307
  return this.getDocument(newRealPath);
4918
5308
  }
4919
5309
  if (alreadyExists === false) {
4920
- throw new Error(`Unable to update document, ${realPath} does not exist`);
5310
+ throw new Error(
5311
+ `Unable to update document, ${realPath} does not exist`
5312
+ );
4921
5313
  }
4922
5314
  return this.updateResolveDocument({
4923
5315
  collection,
@@ -4927,7 +5319,10 @@ var Resolver = class {
4927
5319
  isCollectionSpecific
4928
5320
  });
4929
5321
  } else {
4930
- return this.getDocument(realPath);
5322
+ return this.getDocument(realPath, {
5323
+ collection,
5324
+ checkReferences: true
5325
+ });
4931
5326
  }
4932
5327
  };
4933
5328
  this.resolveCollectionConnections = async ({ ids }) => {
@@ -4964,6 +5359,7 @@ var Resolver = class {
4964
5359
  },
4965
5360
  collection: referencedCollection,
4966
5361
  hydrator: (path7) => path7
5362
+ // just return the path
4967
5363
  }
4968
5364
  );
4969
5365
  const { edges } = resolvedCollectionConnection;
@@ -5031,6 +5427,82 @@ var Resolver = class {
5031
5427
  }
5032
5428
  };
5033
5429
  };
5430
+ /**
5431
+ * Checks if a document has references to it
5432
+ * @param id The id of the document to check for references
5433
+ * @param c The collection to check for references
5434
+ * @returns true if the document has references, false otherwise
5435
+ */
5436
+ this.hasReferences = async (id, c) => {
5437
+ let count = 0;
5438
+ await this.database.query(
5439
+ {
5440
+ collection: c.name,
5441
+ filterChain: makeFilterChain({
5442
+ conditions: [
5443
+ {
5444
+ filterPath: REFS_REFERENCE_FIELD,
5445
+ filterExpression: {
5446
+ _type: "string",
5447
+ _list: false,
5448
+ eq: id
5449
+ }
5450
+ }
5451
+ ]
5452
+ }),
5453
+ sort: REFS_COLLECTIONS_SORT_KEY
5454
+ },
5455
+ (refId) => {
5456
+ count++;
5457
+ return refId;
5458
+ }
5459
+ );
5460
+ if (count) {
5461
+ return true;
5462
+ }
5463
+ return false;
5464
+ };
5465
+ /**
5466
+ * Finds references to a document
5467
+ * @param id the id of the document to find references to
5468
+ * @param c the collection to find references in
5469
+ * @returns a map of references to the document
5470
+ */
5471
+ this.findReferences = async (id, c) => {
5472
+ const references = {};
5473
+ await this.database.query(
5474
+ {
5475
+ collection: c.name,
5476
+ filterChain: makeFilterChain({
5477
+ conditions: [
5478
+ {
5479
+ filterPath: REFS_REFERENCE_FIELD,
5480
+ filterExpression: {
5481
+ _type: "string",
5482
+ _list: false,
5483
+ eq: id
5484
+ }
5485
+ }
5486
+ ]
5487
+ }),
5488
+ sort: REFS_COLLECTIONS_SORT_KEY
5489
+ },
5490
+ (refId, rawItem) => {
5491
+ if (!references[c.name]) {
5492
+ references[c.name] = {};
5493
+ }
5494
+ if (!references[c.name][refId]) {
5495
+ references[c.name][refId] = [];
5496
+ }
5497
+ const referencePath = rawItem?.[REFS_PATH_FIELD];
5498
+ if (referencePath) {
5499
+ references[c.name][refId].push(referencePath);
5500
+ }
5501
+ return refId;
5502
+ }
5503
+ );
5504
+ return references;
5505
+ };
5034
5506
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
5035
5507
  const accum = {};
5036
5508
  for (const passwordField of template.fields.filter(
@@ -5098,7 +5570,7 @@ var Resolver = class {
5098
5570
  }
5099
5571
  break;
5100
5572
  case "rich-text":
5101
- accum[fieldName] = stringifyMDX(
5573
+ accum[fieldName] = serializeMDX(
5102
5574
  fieldValue,
5103
5575
  field,
5104
5576
  (fieldValue2) => resolveMediaCloudToRelative(
@@ -5117,6 +5589,27 @@ var Resolver = class {
5117
5589
  }
5118
5590
  return accum;
5119
5591
  };
5592
+ /**
5593
+ * A mutation looks nearly identical between updateDocument:
5594
+ * ```graphql
5595
+ * updateDocument(collection: $collection,relativePath: $path, params: {
5596
+ * post: {
5597
+ * title: "Hello, World"
5598
+ * }
5599
+ * })`
5600
+ * ```
5601
+ * and `updatePostDocument`:
5602
+ * ```graphql
5603
+ * updatePostDocument(relativePath: $path, params: {
5604
+ * title: "Hello, World"
5605
+ * })
5606
+ * ```
5607
+ * The problem here is that we don't know whether the payload came from `updateDocument`
5608
+ * or `updatePostDocument` (we could, but for now it's easier not to pipe those details through),
5609
+ * But we do know that when given a `args.collection` value, we can assume that
5610
+ * this was a `updateDocument` request, and thus - should grab the data
5611
+ * from the corresponding field name in the key
5612
+ */
5120
5613
  this.buildParams = (args) => {
5121
5614
  try {
5122
5615
  assertShape(
@@ -5184,8 +5677,129 @@ var resolveDateInput = (value) => {
5184
5677
  return date;
5185
5678
  };
5186
5679
 
5187
- // src/resolve.ts
5680
+ // src/resolver/auth-fields.ts
5188
5681
  import set from "lodash.set";
5682
+ async function getUserDocumentContext(tinaSchema, resolver) {
5683
+ const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5684
+ if (!collection) {
5685
+ throw new Error("Auth collection not found");
5686
+ }
5687
+ const userFields = mapUserFields(collection, ["_rawData"]);
5688
+ if (!userFields.length) {
5689
+ throw new Error(`No user field found in collection ${collection.name}`);
5690
+ }
5691
+ if (userFields.length > 1) {
5692
+ throw new Error(
5693
+ `Multiple user fields found in collection ${collection.name}`
5694
+ );
5695
+ }
5696
+ const userField = userFields[0];
5697
+ const realPath = `${collection.path}/index.json`;
5698
+ const userDoc = await resolver.getDocument(realPath);
5699
+ const users = get(userDoc, userField.path);
5700
+ if (!users) {
5701
+ throw new Error("No users found");
5702
+ }
5703
+ return { collection, userField, users, userDoc, realPath };
5704
+ }
5705
+ function findUserInCollection(users, userField, userSub) {
5706
+ const { idFieldName } = userField;
5707
+ if (!idFieldName) {
5708
+ throw new Error("No uid field found on user field");
5709
+ }
5710
+ return users.find((u) => u[idFieldName] === userSub) || null;
5711
+ }
5712
+ async function handleAuthenticate({
5713
+ tinaSchema,
5714
+ resolver,
5715
+ sub,
5716
+ password,
5717
+ ctxUser
5718
+ }) {
5719
+ const userSub = sub || ctxUser?.sub;
5720
+ const { userField, users } = await getUserDocumentContext(
5721
+ tinaSchema,
5722
+ resolver
5723
+ );
5724
+ const user = findUserInCollection(users, userField, userSub);
5725
+ if (!user) {
5726
+ return null;
5727
+ }
5728
+ const { passwordFieldName } = userField;
5729
+ const saltedHash = get(user, [passwordFieldName || "", "value"]);
5730
+ if (!saltedHash) {
5731
+ throw new Error("No password field found on user field");
5732
+ }
5733
+ const matches = await checkPasswordHash({
5734
+ saltedHash,
5735
+ password
5736
+ });
5737
+ return matches ? user : null;
5738
+ }
5739
+ async function handleAuthorize({
5740
+ tinaSchema,
5741
+ resolver,
5742
+ sub,
5743
+ ctxUser
5744
+ }) {
5745
+ const userSub = sub || ctxUser?.sub;
5746
+ const { userField, users } = await getUserDocumentContext(
5747
+ tinaSchema,
5748
+ resolver
5749
+ );
5750
+ const user = findUserInCollection(users, userField, userSub);
5751
+ return user ? user : null;
5752
+ }
5753
+ async function handleUpdatePassword({
5754
+ tinaSchema,
5755
+ resolver,
5756
+ password,
5757
+ ctxUser
5758
+ }) {
5759
+ if (!ctxUser?.sub) {
5760
+ throw new Error("Not authorized");
5761
+ }
5762
+ if (!password) {
5763
+ throw new Error("No password provided");
5764
+ }
5765
+ const { collection, userField, users, realPath } = await getUserDocumentContext(tinaSchema, resolver);
5766
+ const { idFieldName, passwordFieldName } = userField;
5767
+ const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5768
+ if (!user) {
5769
+ throw new Error("Not authorized");
5770
+ }
5771
+ user[passwordFieldName] = {
5772
+ value: password,
5773
+ passwordChangeRequired: false
5774
+ };
5775
+ const params = {};
5776
+ set(
5777
+ params,
5778
+ userField.path.slice(1),
5779
+ // remove _rawData from users path
5780
+ users.map((u) => {
5781
+ if (user[idFieldName] === u[idFieldName]) {
5782
+ return user;
5783
+ }
5784
+ return {
5785
+ // don't overwrite other users' passwords
5786
+ ...u,
5787
+ [passwordFieldName]: {
5788
+ ...u[passwordFieldName],
5789
+ value: ""
5790
+ }
5791
+ };
5792
+ })
5793
+ );
5794
+ await resolver.updateResolveDocument({
5795
+ collection,
5796
+ args: { params },
5797
+ realPath,
5798
+ isCollectionSpecific: true,
5799
+ isAddPendingDocument: false
5800
+ });
5801
+ return true;
5802
+ }
5189
5803
 
5190
5804
  // src/error.ts
5191
5805
  import { GraphQLError as GraphQLError3 } from "graphql";
@@ -5216,7 +5830,10 @@ var resolve = async ({
5216
5830
  const graphQLSchema = buildASTSchema(graphQLSchemaAst);
5217
5831
  const tinaConfig = await database.getTinaSchema();
5218
5832
  const tinaSchema = await createSchema({
5833
+ // TODO: please update all the types to import from @tinacms/schema-tools
5834
+ // @ts-ignore
5219
5835
  schema: tinaConfig,
5836
+ // @ts-ignore
5220
5837
  flags: tinaConfig?.meta?.flags
5221
5838
  });
5222
5839
  const resolver = createResolver({
@@ -5233,8 +5850,7 @@ var resolve = async ({
5233
5850
  database
5234
5851
  },
5235
5852
  typeResolver: async (source, _args, info) => {
5236
- if (source.__typename)
5237
- return source.__typename;
5853
+ if (source.__typename) return source.__typename;
5238
5854
  const namedType = getNamedType(info.returnType).toString();
5239
5855
  const lookup = await database.getLookup(namedType);
5240
5856
  if (lookup.resolveType === "unionData") {
@@ -5293,123 +5909,42 @@ var resolve = async ({
5293
5909
  );
5294
5910
  }
5295
5911
  }
5296
- if (info.fieldName === "authenticate" || info.fieldName === "authorize") {
5297
- const sub = args.sub || ctxUser?.sub;
5298
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5299
- if (!collection) {
5300
- throw new Error("Auth collection not found");
5301
- }
5302
- const userFields = mapUserFields(collection, ["_rawData"]);
5303
- if (!userFields.length) {
5304
- throw new Error(
5305
- `No user field found in collection ${collection.name}`
5306
- );
5307
- }
5308
- if (userFields.length > 1) {
5309
- throw new Error(
5310
- `Multiple user fields found in collection ${collection.name}`
5311
- );
5312
- }
5313
- const userField = userFields[0];
5314
- const realPath = `${collection.path}/index.json`;
5315
- const userDoc = await resolver.getDocument(realPath);
5316
- const users = get(userDoc, userField.path);
5317
- if (!users) {
5318
- throw new Error("No users found");
5319
- }
5320
- const { idFieldName, passwordFieldName } = userField;
5321
- if (!idFieldName) {
5322
- throw new Error("No uid field found on user field");
5323
- }
5324
- const user = users.find((u) => u[idFieldName] === sub);
5325
- if (!user) {
5326
- return null;
5327
- }
5328
- if (info.fieldName === "authenticate") {
5329
- const saltedHash = get(user, [passwordFieldName || "", "value"]);
5330
- if (!saltedHash) {
5331
- throw new Error("No password field found on user field");
5332
- }
5333
- const matches = await checkPasswordHash({
5334
- saltedHash,
5335
- password: args.password
5336
- });
5337
- if (matches) {
5338
- return user;
5339
- }
5340
- return null;
5341
- }
5342
- return user;
5912
+ if (info.fieldName === "authenticate") {
5913
+ return handleAuthenticate({
5914
+ tinaSchema,
5915
+ resolver,
5916
+ sub: args.sub,
5917
+ password: args.password,
5918
+ info,
5919
+ ctxUser
5920
+ });
5921
+ }
5922
+ if (info.fieldName === "authorize") {
5923
+ return handleAuthorize({
5924
+ tinaSchema,
5925
+ resolver,
5926
+ sub: args.sub,
5927
+ info,
5928
+ ctxUser
5929
+ });
5343
5930
  }
5344
5931
  if (info.fieldName === "updatePassword") {
5345
- if (!ctxUser?.sub) {
5346
- throw new Error("Not authorized");
5347
- }
5348
- if (!args.password) {
5349
- throw new Error("No password provided");
5350
- }
5351
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5352
- if (!collection) {
5353
- throw new Error("Auth collection not found");
5354
- }
5355
- const userFields = mapUserFields(collection, ["_rawData"]);
5356
- if (!userFields.length) {
5357
- throw new Error(
5358
- `No user field found in collection ${collection.name}`
5359
- );
5360
- }
5361
- if (userFields.length > 1) {
5362
- throw new Error(
5363
- `Multiple user fields found in collection ${collection.name}`
5364
- );
5365
- }
5366
- const userField = userFields[0];
5367
- const realPath = `${collection.path}/index.json`;
5368
- const userDoc = await resolver.getDocument(realPath);
5369
- const users = get(userDoc, userField.path);
5370
- if (!users) {
5371
- throw new Error("No users found");
5372
- }
5373
- const { idFieldName, passwordFieldName } = userField;
5374
- const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5375
- if (!user) {
5376
- throw new Error("Not authorized");
5377
- }
5378
- user[passwordFieldName] = {
5379
- value: args.password,
5380
- passwordChangeRequired: false
5381
- };
5382
- const params = {};
5383
- set(
5384
- params,
5385
- userField.path.slice(1),
5386
- users.map((u) => {
5387
- if (user[idFieldName] === u[idFieldName]) {
5388
- return user;
5389
- }
5390
- return {
5391
- ...u,
5392
- [passwordFieldName]: {
5393
- ...u[passwordFieldName],
5394
- value: ""
5395
- }
5396
- };
5397
- })
5398
- );
5399
- await resolver.updateResolveDocument({
5400
- collection,
5401
- args: { params },
5402
- realPath,
5403
- isCollectionSpecific: true,
5404
- isAddPendingDocument: false
5932
+ return handleUpdatePassword({
5933
+ tinaSchema,
5934
+ resolver,
5935
+ password: args.password,
5936
+ info,
5937
+ ctxUser
5405
5938
  });
5406
- return true;
5407
5939
  }
5408
5940
  if (!lookup) {
5409
5941
  return value;
5410
5942
  }
5411
5943
  const isCreation = lookup[info.fieldName] === "create";
5412
5944
  switch (lookup.resolveType) {
5945
+ /**
5946
+ * `node(id: $id)`
5947
+ */
5413
5948
  case "nodeDocument":
5414
5949
  assertShape(
5415
5950
  args,
@@ -5441,6 +5976,7 @@ var resolve = async ({
5441
5976
  collection: args.collection,
5442
5977
  isMutation,
5443
5978
  isCreation,
5979
+ // Right now this is the only case for deletion
5444
5980
  isDeletion: info.fieldName === "deleteDocument",
5445
5981
  isFolderCreation: info.fieldName === "createFolder",
5446
5982
  isUpdateName: Boolean(args?.params?.relativePath),
@@ -5450,6 +5986,9 @@ var resolve = async ({
5450
5986
  return result;
5451
5987
  }
5452
5988
  return value;
5989
+ /**
5990
+ * eg `getMovieDocument.data.actors`
5991
+ */
5453
5992
  case "multiCollectionDocumentList":
5454
5993
  if (Array.isArray(value)) {
5455
5994
  return {
@@ -5461,7 +6000,15 @@ var resolve = async ({
5461
6000
  }
5462
6001
  if (info.fieldName === "documents" && value?.collection && value?.hasDocuments) {
5463
6002
  let filter = args.filter;
5464
- if (typeof args?.filter !== "undefined" && args?.filter !== null && typeof value?.collection?.name === "string" && Object.keys(args.filter).includes(value?.collection?.name) && typeof args.filter[value?.collection?.name] !== "undefined") {
6003
+ if (
6004
+ // 1. Make sure that the filter exists
6005
+ typeof args?.filter !== "undefined" && args?.filter !== null && // 2. Make sure that the collection name exists
6006
+ // @ts-ignore
6007
+ typeof value?.collection?.name === "string" && // 3. Make sure that the collection name is in the filter and is not undefined
6008
+ // @ts-ignore
6009
+ Object.keys(args.filter).includes(value?.collection?.name) && // @ts-ignore
6010
+ typeof args.filter[value?.collection?.name] !== "undefined"
6011
+ ) {
5465
6012
  filter = args.filter[value.collection.name];
5466
6013
  }
5467
6014
  return resolver.resolveCollectionConnection({
@@ -5469,12 +6016,20 @@ var resolve = async ({
5469
6016
  ...args,
5470
6017
  filter
5471
6018
  },
6019
+ // @ts-ignore
5472
6020
  collection: value.collection
5473
6021
  });
5474
6022
  }
5475
6023
  throw new Error(
5476
6024
  `Expected an array for result of ${info.fieldName} at ${info.path}`
5477
6025
  );
6026
+ /**
6027
+ * Collections-specific getter
6028
+ * eg. `getPostDocument`/`createPostDocument`/`updatePostDocument`
6029
+ *
6030
+ * if coming from a query result
6031
+ * the field will be `node`
6032
+ */
5478
6033
  case "collectionDocument": {
5479
6034
  if (value) {
5480
6035
  return value;
@@ -5489,11 +6044,32 @@ var resolve = async ({
5489
6044
  });
5490
6045
  return result;
5491
6046
  }
6047
+ /**
6048
+ * Collections-specific list getter
6049
+ * eg. `getPageList`
6050
+ */
5492
6051
  case "collectionDocumentList":
5493
6052
  return resolver.resolveCollectionConnection({
5494
6053
  args,
5495
6054
  collection: tinaSchema.getCollection(lookup.collection)
5496
6055
  });
6056
+ /**
6057
+ * A polymorphic data set, it can be from a document's data
6058
+ * of any nested object which can be one of many shapes
6059
+ *
6060
+ * ```graphql
6061
+ * getPostDocument(relativePath: $relativePath) {
6062
+ * data {...} <- this part
6063
+ * }
6064
+ * ```
6065
+ * ```graphql
6066
+ * getBlockDocument(relativePath: $relativePath) {
6067
+ * data {
6068
+ * blocks {...} <- or this part
6069
+ * }
6070
+ * }
6071
+ * ```
6072
+ */
5497
6073
  case "unionData":
5498
6074
  if (!value) {
5499
6075
  if (args.relativePath) {
@@ -5558,8 +6134,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
5558
6134
  this.port = port || 9e3;
5559
6135
  }
5560
6136
  openConnection() {
5561
- if (this._connected)
5562
- return;
6137
+ if (this._connected) return;
5563
6138
  const socket = connect(this.port);
5564
6139
  pipeline(socket, this.createRpcStream(), socket, () => {
5565
6140
  this._connected = false;
@@ -5569,7 +6144,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
5569
6144
  };
5570
6145
 
5571
6146
  // src/database/index.ts
5572
- import path4 from "path";
6147
+ import path4 from "node:path";
5573
6148
  import { GraphQLError as GraphQLError5 } from "graphql";
5574
6149
  import micromatch2 from "micromatch";
5575
6150
  import sha2 from "js-sha1";
@@ -5704,6 +6279,7 @@ var Database = class {
5704
6279
  );
5705
6280
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
5706
6281
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6282
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
5707
6283
  const normalizedPath = normalizePath(filepath);
5708
6284
  if (!collection?.isDetached) {
5709
6285
  if (this.bridge) {
@@ -5732,6 +6308,14 @@ var Database = class {
5732
6308
  let delOps = [];
5733
6309
  if (!isGitKeep(normalizedPath, collection)) {
5734
6310
  putOps = [
6311
+ ...makeRefOpsForDocument(
6312
+ normalizedPath,
6313
+ collection?.name,
6314
+ collectionReferences,
6315
+ dataFields,
6316
+ "put",
6317
+ level
6318
+ ),
5735
6319
  ...makeIndexOpsForDocument(
5736
6320
  normalizedPath,
5737
6321
  collection?.name,
@@ -5740,6 +6324,7 @@ var Database = class {
5740
6324
  "put",
5741
6325
  level
5742
6326
  ),
6327
+ // folder indices
5743
6328
  ...makeIndexOpsForDocument(
5744
6329
  normalizedPath,
5745
6330
  `${collection?.name}_${folderKey}`,
@@ -5754,6 +6339,14 @@ var Database = class {
5754
6339
  SUBLEVEL_OPTIONS
5755
6340
  ).get(normalizedPath);
5756
6341
  delOps = existingItem ? [
6342
+ ...makeRefOpsForDocument(
6343
+ normalizedPath,
6344
+ collection?.name,
6345
+ collectionReferences,
6346
+ existingItem,
6347
+ "del",
6348
+ level
6349
+ ),
5757
6350
  ...makeIndexOpsForDocument(
5758
6351
  normalizedPath,
5759
6352
  collection?.name,
@@ -5762,6 +6355,7 @@ var Database = class {
5762
6355
  "del",
5763
6356
  level
5764
6357
  ),
6358
+ // folder indices
5765
6359
  ...makeIndexOpsForDocument(
5766
6360
  normalizedPath,
5767
6361
  `${collection?.name}_${folderKey}`,
@@ -5800,6 +6394,7 @@ var Database = class {
5800
6394
  );
5801
6395
  collectionIndexDefinitions = indexDefinitions?.[collectionName];
5802
6396
  }
6397
+ const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
5803
6398
  const normalizedPath = normalizePath(filepath);
5804
6399
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5805
6400
  const collection = await this.collectionForPath(filepath);
@@ -5847,6 +6442,14 @@ var Database = class {
5847
6442
  let delOps = [];
5848
6443
  if (!isGitKeep(normalizedPath, collection)) {
5849
6444
  putOps = [
6445
+ ...makeRefOpsForDocument(
6446
+ normalizedPath,
6447
+ collectionName,
6448
+ collectionReferences,
6449
+ dataFields,
6450
+ "put",
6451
+ level
6452
+ ),
5850
6453
  ...makeIndexOpsForDocument(
5851
6454
  normalizedPath,
5852
6455
  collectionName,
@@ -5855,6 +6458,7 @@ var Database = class {
5855
6458
  "put",
5856
6459
  level
5857
6460
  ),
6461
+ // folder indices
5858
6462
  ...makeIndexOpsForDocument(
5859
6463
  normalizedPath,
5860
6464
  `${collection?.name}_${folderKey}`,
@@ -5869,6 +6473,14 @@ var Database = class {
5869
6473
  SUBLEVEL_OPTIONS
5870
6474
  ).get(normalizedPath);
5871
6475
  delOps = existingItem ? [
6476
+ ...makeRefOpsForDocument(
6477
+ normalizedPath,
6478
+ collectionName,
6479
+ collectionReferences,
6480
+ existingItem,
6481
+ "del",
6482
+ level
6483
+ ),
5872
6484
  ...makeIndexOpsForDocument(
5873
6485
  normalizedPath,
5874
6486
  collectionName,
@@ -5877,6 +6489,7 @@ var Database = class {
5877
6489
  "del",
5878
6490
  level
5879
6491
  ),
6492
+ // folder indices
5880
6493
  ...makeIndexOpsForDocument(
5881
6494
  normalizedPath,
5882
6495
  `${collection?.name}_${folderKey}`,
@@ -5910,8 +6523,7 @@ var Database = class {
5910
6523
  throw new TinaFetchError(`Error in PUT for ${filepath}`, {
5911
6524
  originalError: error,
5912
6525
  file: filepath,
5913
- collection: collectionName,
5914
- stack: error.stack
6526
+ collection: collectionName
5915
6527
  });
5916
6528
  }
5917
6529
  };
@@ -5954,6 +6566,7 @@ var Database = class {
5954
6566
  aliasedData,
5955
6567
  extension,
5956
6568
  writeTemplateKey,
6569
+ //templateInfo.type === 'union',
5957
6570
  {
5958
6571
  frontmatterFormat: collection?.frontmatterFormat,
5959
6572
  frontmatterDelimiters: collection?.frontmatterDelimiters
@@ -5992,6 +6605,7 @@ var Database = class {
5992
6605
  SUBLEVEL_OPTIONS
5993
6606
  ).get(graphqlPath);
5994
6607
  };
6608
+ //TODO - is there a reason why the database fetches some config with "bridge.get", and some with "store.get"?
5995
6609
  this.getGraphQLSchemaFromBridge = async () => {
5996
6610
  if (!this.bridge) {
5997
6611
  throw new Error(`No bridge configured`);
@@ -6028,6 +6642,22 @@ var Database = class {
6028
6642
  this.tinaSchema = await createSchema({ schema });
6029
6643
  return this.tinaSchema;
6030
6644
  };
6645
+ this.getCollectionReferences = async (level) => {
6646
+ if (this.collectionReferences) {
6647
+ return this.collectionReferences;
6648
+ }
6649
+ const result = {};
6650
+ const schema = await this.getSchema(level || this.contentLevel);
6651
+ const collections = schema.getCollections();
6652
+ for (const collection of collections) {
6653
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6654
+ collection.name
6655
+ );
6656
+ result[collection.name] = collectionReferences;
6657
+ }
6658
+ this.collectionReferences = result;
6659
+ return result;
6660
+ };
6031
6661
  this.getIndexDefinitions = async (level) => {
6032
6662
  if (!this.collectionIndexDefinitions) {
6033
6663
  await new Promise(async (resolve2, reject) => {
@@ -6037,10 +6667,53 @@ var Database = class {
6037
6667
  const collections = schema.getCollections();
6038
6668
  for (const collection of collections) {
6039
6669
  const indexDefinitions = {
6040
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6670
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6671
+ // provide a default sort key which is the file sort
6672
+ // pseudo-index for the collection's references
6673
+ [REFS_COLLECTIONS_SORT_KEY]: {
6674
+ fields: [
6675
+ {
6676
+ name: REFS_REFERENCE_FIELD,
6677
+ type: "string",
6678
+ list: false
6679
+ },
6680
+ {
6681
+ name: REFS_PATH_FIELD,
6682
+ type: "string",
6683
+ list: false
6684
+ }
6685
+ ]
6686
+ }
6041
6687
  };
6042
- if (collection.fields) {
6043
- for (const field of collection.fields) {
6688
+ let fields = [];
6689
+ if (collection.templates) {
6690
+ const templateFieldMap = {};
6691
+ const conflictedFields = /* @__PURE__ */ new Set();
6692
+ for (const template of collection.templates) {
6693
+ for (const field of template.fields) {
6694
+ if (!templateFieldMap[field.name]) {
6695
+ templateFieldMap[field.name] = field;
6696
+ } else {
6697
+ if (templateFieldMap[field.name].type !== field.type) {
6698
+ console.warn(
6699
+ `Field ${field.name} has conflicting types in templates - skipping index`
6700
+ );
6701
+ conflictedFields.add(field.name);
6702
+ }
6703
+ }
6704
+ }
6705
+ }
6706
+ for (const conflictedField in conflictedFields) {
6707
+ delete templateFieldMap[conflictedField];
6708
+ }
6709
+ for (const field of Object.values(templateFieldMap)) {
6710
+ fields.push(field);
6711
+ }
6712
+ } else if (collection.fields) {
6713
+ fields = collection.fields;
6714
+ }
6715
+ if (fields) {
6716
+ for (const field of fields) {
6044
6717
  if (field.indexed !== void 0 && field.indexed === false || field.type === "object") {
6045
6718
  continue;
6046
6719
  }
@@ -6188,29 +6861,35 @@ var Database = class {
6188
6861
  }
6189
6862
  startKey = startKey || key || "";
6190
6863
  endKey = key || "";
6191
- edges = [...edges, { cursor: key, path: filepath }];
6864
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6192
6865
  }
6193
6866
  return {
6194
- edges: await sequential(edges, async (edge) => {
6195
- try {
6196
- const node = await hydrator(edge.path);
6197
- return {
6198
- node,
6199
- cursor: btoa(edge.cursor)
6200
- };
6201
- } catch (error) {
6202
- console.log(error);
6203
- if (error instanceof Error && (!edge.path.includes(".tina/__generated__/_graphql.json") || !edge.path.includes("tina/__generated__/_graphql.json"))) {
6204
- throw new TinaQueryError({
6205
- originalError: error,
6206
- file: edge.path,
6207
- collection: collection.name,
6208
- stack: error.stack
6209
- });
6867
+ edges: await sequential(
6868
+ edges,
6869
+ async ({
6870
+ cursor,
6871
+ path: path7,
6872
+ value
6873
+ }) => {
6874
+ try {
6875
+ const node = await hydrator(path7, value);
6876
+ return {
6877
+ node,
6878
+ cursor: btoa(cursor)
6879
+ };
6880
+ } catch (error) {
6881
+ console.log(error);
6882
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6883
+ throw new TinaQueryError({
6884
+ originalError: error,
6885
+ file: path7,
6886
+ collection: collection.name
6887
+ });
6888
+ }
6889
+ throw error;
6210
6890
  }
6211
- throw error;
6212
6891
  }
6213
- }),
6892
+ ),
6214
6893
  pageInfo: {
6215
6894
  hasPreviousPage,
6216
6895
  hasNextPage,
@@ -6334,13 +7013,14 @@ var Database = class {
6334
7013
  documentPaths,
6335
7014
  async (collection, documentPaths2) => {
6336
7015
  if (collection && !collection.isDetached) {
6337
- await _indexContent(
6338
- this,
6339
- this.contentLevel,
6340
- documentPaths2,
7016
+ await _indexContent({
7017
+ database: this,
7018
+ level: this.contentLevel,
7019
+ documentPaths: documentPaths2,
6341
7020
  enqueueOps,
6342
- collection
6343
- );
7021
+ collection,
7022
+ isPartialReindex: true
7023
+ });
6344
7024
  }
6345
7025
  }
6346
7026
  );
@@ -6356,17 +7036,18 @@ var Database = class {
6356
7036
  throw new Error(`No collection found for path: ${filepath}`);
6357
7037
  }
6358
7038
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7039
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6359
7040
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6360
7041
  let level = this.contentLevel;
6361
7042
  if (collection?.isDetached) {
6362
7043
  level = this.appLevel.sublevel(collection?.name, SUBLEVEL_OPTIONS);
6363
7044
  }
6364
- const itemKey = normalizePath(filepath);
7045
+ const normalizedPath = normalizePath(filepath);
6365
7046
  const rootSublevel = level.sublevel(
6366
7047
  CONTENT_ROOT_PREFIX,
6367
7048
  SUBLEVEL_OPTIONS
6368
7049
  );
6369
- const item = await rootSublevel.get(itemKey);
7050
+ const item = await rootSublevel.get(normalizedPath);
6370
7051
  if (item) {
6371
7052
  const folderTreeBuilder = new FolderTreeBuilder();
6372
7053
  const folderKey = folderTreeBuilder.update(
@@ -6374,16 +7055,25 @@ var Database = class {
6374
7055
  collection.path || ""
6375
7056
  );
6376
7057
  await this.contentLevel.batch([
7058
+ ...makeRefOpsForDocument(
7059
+ normalizedPath,
7060
+ collection.name,
7061
+ collectionReferences,
7062
+ item,
7063
+ "del",
7064
+ level
7065
+ ),
6377
7066
  ...makeIndexOpsForDocument(
6378
- filepath,
7067
+ normalizedPath,
6379
7068
  collection.name,
6380
7069
  collectionIndexDefinitions,
6381
7070
  item,
6382
7071
  "del",
6383
7072
  level
6384
7073
  ),
7074
+ // folder indices
6385
7075
  ...makeIndexOpsForDocument(
6386
- filepath,
7076
+ normalizedPath,
6387
7077
  `${collection.name}_${folderKey}`,
6388
7078
  collectionIndexDefinitions,
6389
7079
  item,
@@ -6392,17 +7082,17 @@ var Database = class {
6392
7082
  ),
6393
7083
  {
6394
7084
  type: "del",
6395
- key: itemKey,
7085
+ key: normalizedPath,
6396
7086
  sublevel: rootSublevel
6397
7087
  }
6398
7088
  ]);
6399
7089
  }
6400
7090
  if (!collection?.isDetached) {
6401
7091
  if (this.bridge) {
6402
- await this.bridge.delete(normalizePath(filepath));
7092
+ await this.bridge.delete(normalizedPath);
6403
7093
  }
6404
7094
  try {
6405
- await this.onDelete(normalizePath(filepath));
7095
+ await this.onDelete(normalizedPath);
6406
7096
  } catch (e) {
6407
7097
  throw new GraphQLError5(
6408
7098
  `Error running onDelete hook for ${filepath}: ${e}`,
@@ -6437,20 +7127,26 @@ var Database = class {
6437
7127
  );
6438
7128
  const doc = await level2.keys({ limit: 1 }).next();
6439
7129
  if (!doc) {
6440
- await _indexContent(
6441
- this,
6442
- level2,
6443
- contentPaths,
7130
+ await _indexContent({
7131
+ database: this,
7132
+ level: level2,
7133
+ documentPaths: contentPaths,
6444
7134
  enqueueOps,
6445
7135
  collection,
6446
- userFields.map((field) => [
7136
+ passwordFields: userFields.map((field) => [
6447
7137
  ...field.path,
6448
7138
  field.passwordFieldName
6449
7139
  ])
6450
- );
7140
+ });
6451
7141
  }
6452
7142
  } else {
6453
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7143
+ await _indexContent({
7144
+ database: this,
7145
+ level,
7146
+ documentPaths: contentPaths,
7147
+ enqueueOps,
7148
+ collection
7149
+ });
6454
7150
  }
6455
7151
  }
6456
7152
  );
@@ -6536,6 +7232,9 @@ var Database = class {
6536
7232
  info: templateInfo
6537
7233
  };
6538
7234
  }
7235
+ /**
7236
+ * Clears the internal cache of the tinaSchema and the lookup file. This allows the state to be reset
7237
+ */
6539
7238
  clearCache() {
6540
7239
  this.tinaSchema = null;
6541
7240
  this._lookup = null;
@@ -6586,7 +7285,15 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
6586
7285
  )
6587
7286
  );
6588
7287
  var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${collection?.format || "md"}`);
6589
- var _indexContent = async (database, level, documentPaths, enqueueOps, collection, passwordFields) => {
7288
+ var _indexContent = async ({
7289
+ database,
7290
+ level,
7291
+ documentPaths,
7292
+ enqueueOps,
7293
+ collection,
7294
+ passwordFields,
7295
+ isPartialReindex
7296
+ }) => {
6590
7297
  let collectionIndexDefinitions;
6591
7298
  let collectionPath;
6592
7299
  if (collection) {
@@ -6597,6 +7304,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6597
7304
  }
6598
7305
  collectionPath = collection.path;
6599
7306
  }
7307
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
6600
7308
  const tinaSchema = await database.getSchema();
6601
7309
  let templateInfo = null;
6602
7310
  if (collection) {
@@ -6618,12 +7326,61 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6618
7326
  await hashPasswordValues(aliasedData, passwordFields);
6619
7327
  }
6620
7328
  const normalizedPath = normalizePath(filepath);
7329
+ const rootSublevel = level.sublevel(
7330
+ CONTENT_ROOT_PREFIX,
7331
+ SUBLEVEL_OPTIONS
7332
+ );
6621
7333
  const folderKey = folderTreeBuilder.update(
6622
7334
  normalizedPath,
6623
7335
  collectionPath || ""
6624
7336
  );
7337
+ if (isPartialReindex) {
7338
+ const item = await rootSublevel.get(normalizedPath);
7339
+ if (item) {
7340
+ await database.contentLevel.batch([
7341
+ ...makeRefOpsForDocument(
7342
+ normalizedPath,
7343
+ collection?.name,
7344
+ collectionReferences,
7345
+ item,
7346
+ "del",
7347
+ level
7348
+ ),
7349
+ ...makeIndexOpsForDocument(
7350
+ normalizedPath,
7351
+ collection.name,
7352
+ collectionIndexDefinitions,
7353
+ item,
7354
+ "del",
7355
+ level
7356
+ ),
7357
+ // folder indices
7358
+ ...makeIndexOpsForDocument(
7359
+ normalizedPath,
7360
+ `${collection.name}_${folderKey}`,
7361
+ collectionIndexDefinitions,
7362
+ item,
7363
+ "del",
7364
+ level
7365
+ ),
7366
+ {
7367
+ type: "del",
7368
+ key: normalizedPath,
7369
+ sublevel: rootSublevel
7370
+ }
7371
+ ]);
7372
+ }
7373
+ }
6625
7374
  if (!isGitKeep(filepath, collection)) {
6626
7375
  await enqueueOps([
7376
+ ...makeRefOpsForDocument(
7377
+ normalizedPath,
7378
+ collection?.name,
7379
+ collectionReferences,
7380
+ aliasedData,
7381
+ "put",
7382
+ level
7383
+ ),
6627
7384
  ...makeIndexOpsForDocument(
6628
7385
  normalizedPath,
6629
7386
  collection?.name,
@@ -6632,6 +7389,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6632
7389
  "put",
6633
7390
  level
6634
7391
  ),
7392
+ // folder indexes
6635
7393
  ...makeIndexOpsForDocument(
6636
7394
  normalizedPath,
6637
7395
  `${collection?.name}_${folderKey}`,
@@ -6655,8 +7413,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6655
7413
  throw new TinaFetchError(`Unable to seed ${filepath}`, {
6656
7414
  originalError: error,
6657
7415
  file: filepath,
6658
- collection: collection?.name,
6659
- stack: error.stack
7416
+ collection: collection?.name
6660
7417
  });
6661
7418
  }
6662
7419
  });
@@ -6686,6 +7443,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6686
7443
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6687
7444
  }
6688
7445
  }
7446
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
6689
7447
  const tinaSchema = await database.getSchema();
6690
7448
  let templateInfo = null;
6691
7449
  if (collection) {
@@ -6709,6 +7467,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6709
7467
  item
6710
7468
  ) : item;
6711
7469
  await enqueueOps([
7470
+ ...makeRefOpsForDocument(
7471
+ itemKey,
7472
+ collection?.name,
7473
+ collectionReferences,
7474
+ aliasedData,
7475
+ "del",
7476
+ database.contentLevel
7477
+ ),
6712
7478
  ...makeIndexOpsForDocument(
6713
7479
  itemKey,
6714
7480
  collection.name,
@@ -6717,6 +7483,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6717
7483
  "del",
6718
7484
  database.contentLevel
6719
7485
  ),
7486
+ // folder indexes
6720
7487
  ...makeIndexOpsForDocument(
6721
7488
  itemKey,
6722
7489
  `${collection?.name}_${folderKey}`,
@@ -6837,8 +7604,8 @@ import path6 from "path";
6837
7604
  import normalize from "normalize-path";
6838
7605
  var FilesystemBridge = class {
6839
7606
  constructor(rootPath, outputPath) {
6840
- this.rootPath = rootPath || "";
6841
- this.outputPath = outputPath || rootPath;
7607
+ this.rootPath = path6.resolve(rootPath);
7608
+ this.outputPath = outputPath ? path6.resolve(outputPath) : this.rootPath;
6842
7609
  }
6843
7610
  async glob(pattern, extension) {
6844
7611
  const basePath = path6.join(this.outputPath, ...pattern.split("/"));
@@ -6850,19 +7617,19 @@ var FilesystemBridge = class {
6850
7617
  }
6851
7618
  );
6852
7619
  const posixRootPath = normalize(this.outputPath);
6853
- return items.map((item) => {
6854
- return item.replace(posixRootPath, "").replace(/^\/|\/$/g, "");
6855
- });
7620
+ return items.map(
7621
+ (item) => item.substring(posixRootPath.length).replace(/^\/|\/$/g, "")
7622
+ );
6856
7623
  }
6857
7624
  async delete(filepath) {
6858
7625
  await fs2.remove(path6.join(this.outputPath, filepath));
6859
7626
  }
6860
7627
  async get(filepath) {
6861
- return fs2.readFileSync(path6.join(this.outputPath, filepath)).toString();
7628
+ return (await fs2.readFile(path6.join(this.outputPath, filepath))).toString();
6862
7629
  }
6863
7630
  async put(filepath, data, basePathOverride) {
6864
7631
  const basePath = basePathOverride || this.outputPath;
6865
- await fs2.outputFileSync(path6.join(basePath, filepath), data);
7632
+ await fs2.outputFile(path6.join(basePath, filepath), data);
6866
7633
  }
6867
7634
  };
6868
7635
  var AuditFileSystemBridge = class extends FilesystemBridge {
@@ -6932,17 +7699,26 @@ var IsomorphicBridge = class {
6932
7699
  getAuthor() {
6933
7700
  return {
6934
7701
  ...this.author,
6935
- timestamp: Math.round(new Date().getTime() / 1e3),
7702
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
6936
7703
  timezoneOffset: 0
6937
7704
  };
6938
7705
  }
6939
7706
  getCommitter() {
6940
7707
  return {
6941
7708
  ...this.committer,
6942
- timestamp: Math.round(new Date().getTime() / 1e3),
7709
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
6943
7710
  timezoneOffset: 0
6944
7711
  };
6945
7712
  }
7713
+ /**
7714
+ * Recursively populate paths matching `pattern` for the given `entry`
7715
+ *
7716
+ * @param pattern - pattern to filter paths by
7717
+ * @param entry - TreeEntry to start building list from
7718
+ * @param path - base path
7719
+ * @param results
7720
+ * @private
7721
+ */
6946
7722
  async listEntries({
6947
7723
  pattern,
6948
7724
  entry,
@@ -6975,6 +7751,15 @@ var IsomorphicBridge = class {
6975
7751
  });
6976
7752
  }
6977
7753
  }
7754
+ /**
7755
+ * For the specified path, returns an object with an array containing the parts of the path (pathParts)
7756
+ * and an array containing the WalkerEntry objects for the path parts (pathEntries). Any null elements in the
7757
+ * pathEntries are placeholders for non-existent entries.
7758
+ *
7759
+ * @param path - path being resolved
7760
+ * @param ref - ref to resolve path entries for
7761
+ * @private
7762
+ */
6978
7763
  async resolvePathEntries(path7, ref) {
6979
7764
  let pathParts = path7.split("/");
6980
7765
  const result = await git2.walk({
@@ -7005,6 +7790,17 @@ var IsomorphicBridge = class {
7005
7790
  }
7006
7791
  return { pathParts, pathEntries };
7007
7792
  }
7793
+ /**
7794
+ * Updates tree entry and associated parent tree entries
7795
+ *
7796
+ * @param existingOid - the existing OID
7797
+ * @param updatedOid - the updated OID
7798
+ * @param path - the path of the entry being updated
7799
+ * @param type - the type of the entry being updated (blob or tree)
7800
+ * @param pathEntries - parent path entries
7801
+ * @param pathParts - parent path parts
7802
+ * @private
7803
+ */
7008
7804
  async updateTreeHierarchy(existingOid, updatedOid, path7, type, pathEntries, pathParts) {
7009
7805
  const lastIdx = pathEntries.length - 1;
7010
7806
  const parentEntry = pathEntries[lastIdx];
@@ -7060,6 +7856,13 @@ var IsomorphicBridge = class {
7060
7856
  );
7061
7857
  }
7062
7858
  }
7859
+ /**
7860
+ * Creates a commit for the specified tree and updates the specified ref to point to the commit
7861
+ *
7862
+ * @param treeSha - sha of the new tree
7863
+ * @param ref - the ref that should be updated
7864
+ * @private
7865
+ */
7063
7866
  async commitTree(treeSha, ref) {
7064
7867
  const commitSha = await git2.writeCommit({
7065
7868
  ...this.isomorphicConfig,
@@ -7072,6 +7875,7 @@ var IsomorphicBridge = class {
7072
7875
  })
7073
7876
  ],
7074
7877
  message: this.commitMessage,
7878
+ // TODO these should be configurable
7075
7879
  author: this.getAuthor(),
7076
7880
  committer: this.getCommitter()
7077
7881
  }
@@ -7309,5 +8113,5 @@ export {
7309
8113
  transformDocument,
7310
8114
  transformDocumentIntoPayload
7311
8115
  };
7312
- //! Replaces _.flattenDeep()
7313
8116
  //! Replaces _.get()
8117
+ //! Replaces _.flattenDeep()