@tinacms/graphql 0.0.0-d69e892-20241003042309 → 0.0.0-d9672bc-20250218033222

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 Tina Cloud 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
  });
@@ -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.4",
3022
+ version: "1.5.12",
2819
3023
  main: "dist/index.js",
2820
3024
  module: "dist/index.mjs",
2821
3025
  typings: "dist/index.d.ts",
@@ -2842,8 +3046,8 @@ var package_default = {
2842
3046
  build: "tinacms-scripts build",
2843
3047
  docs: "pnpm typedoc",
2844
3048
  serve: "pnpm nodemon dist/server.js",
2845
- test: "jest",
2846
- "test-watch": "jest --watch"
3049
+ test: "vitest run",
3050
+ "test-watch": "vitest"
2847
3051
  },
2848
3052
  dependencies: {
2849
3053
  "@iarna/toml": "^2.2.5",
@@ -2859,7 +3063,7 @@ var package_default = {
2859
3063
  "isomorphic-git": "^1.27.1",
2860
3064
  "js-sha1": "^0.6.0",
2861
3065
  "js-yaml": "^3.14.1",
2862
- "jsonpath-plus": "^6.0.1",
3066
+ "jsonpath-plus": "10.1.0",
2863
3067
  "lodash.clonedeep": "^4.5.0",
2864
3068
  "lodash.set": "^4.3.2",
2865
3069
  "lodash.uniqby": "^4.7.0",
@@ -2884,24 +3088,23 @@ var package_default = {
2884
3088
  "@types/estree": "^0.0.50",
2885
3089
  "@types/express": "^4.17.21",
2886
3090
  "@types/fs-extra": "^9.0.13",
2887
- "@types/jest": "^26.0.24",
2888
3091
  "@types/js-yaml": "^3.12.10",
2889
3092
  "@types/lodash.camelcase": "^4.3.9",
2890
3093
  "@types/lodash.upperfirst": "^4.3.9",
2891
3094
  "@types/lru-cache": "^5.1.1",
2892
3095
  "@types/mdast": "^3.0.15",
2893
3096
  "@types/micromatch": "^4.0.9",
2894
- "@types/node": "^22.7.4",
3097
+ "@types/node": "^22.9.0",
2895
3098
  "@types/normalize-path": "^3.0.2",
2896
3099
  "@types/ws": "^7.4.7",
2897
3100
  "@types/yup": "^0.29.14",
2898
- jest: "^29.7.0",
2899
- "jest-diff": "^29.7.0",
2900
3101
  "jest-file-snapshot": "^0.5.0",
2901
- "jest-matcher-utils": "^29.7.0",
2902
3102
  "memory-level": "^1.0.0",
2903
3103
  nodemon: "3.1.4",
2904
- typescript: "^5.6.2"
3104
+ typescript: "^5.6.3",
3105
+ vite: "^4.3.9",
3106
+ vitest: "^0.32.2",
3107
+ zod: "^3.23.8"
2905
3108
  }
2906
3109
  };
2907
3110
 
@@ -2972,6 +3175,7 @@ var _buildFragments = async (builder, tinaSchema) => {
2972
3175
  const fragDoc = {
2973
3176
  kind: "Document",
2974
3177
  definitions: uniqBy2(
3178
+ // @ts-ignore
2975
3179
  extractInlineTypes(fragmentDefinitionsFields),
2976
3180
  (node) => node.name.value
2977
3181
  )
@@ -2994,6 +3198,7 @@ var _buildQueries = async (builder, tinaSchema) => {
2994
3198
  fragName,
2995
3199
  queryName: queryListName,
2996
3200
  filterType: queryFilterTypeName,
3201
+ // look for flag to see if the data layer is enabled
2997
3202
  dataLayer: Boolean(
2998
3203
  tinaSchema.config?.meta?.flags?.find((x) => x === "experimentalData")
2999
3204
  )
@@ -3003,6 +3208,7 @@ var _buildQueries = async (builder, tinaSchema) => {
3003
3208
  const queryDoc = {
3004
3209
  kind: "Document",
3005
3210
  definitions: uniqBy2(
3211
+ // @ts-ignore
3006
3212
  extractInlineTypes(operationsDefinitions),
3007
3213
  (node) => node.name.value
3008
3214
  )
@@ -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
  )
@@ -3107,6 +3314,9 @@ import isValid from "date-fns/isValid/index.js";
3107
3314
  // src/mdx/index.ts
3108
3315
  import { parseMDX, stringifyMDX } from "@tinacms/mdx";
3109
3316
 
3317
+ // src/resolver/index.ts
3318
+ import { JSONPath as JSONPath2 } from "jsonpath-plus";
3319
+
3110
3320
  // src/resolver/error.ts
3111
3321
  var TinaGraphQLError = class extends Error {
3112
3322
  constructor(message, extensions) {
@@ -3292,8 +3502,7 @@ var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, s
3292
3502
  }
3293
3503
  if (Array.isArray(value)) {
3294
3504
  return value.map((v) => {
3295
- if (!v || typeof v !== "string")
3296
- return v;
3505
+ if (!v || typeof v !== "string") return v;
3297
3506
  const cleanMediaRoot = cleanUpSlashes(
3298
3507
  schema.config.media.tina.mediaRoot
3299
3508
  );
@@ -3321,8 +3530,7 @@ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, s
3321
3530
  }
3322
3531
  if (Array.isArray(value)) {
3323
3532
  return value.map((v) => {
3324
- if (!v || typeof v !== "string")
3325
- return v;
3533
+ if (!v || typeof v !== "string") return v;
3326
3534
  const strippedValue = v.replace(cleanMediaRoot, "");
3327
3535
  return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3328
3536
  });
@@ -3340,8 +3548,7 @@ var cleanUpSlashes = (path7) => {
3340
3548
  return "";
3341
3549
  };
3342
3550
  var hasTinaMediaConfig = (schema) => {
3343
- if (!schema.config?.media?.tina)
3344
- return false;
3551
+ if (!schema.config?.media?.tina) return false;
3345
3552
  if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
3346
3553
  return false;
3347
3554
  return true;
@@ -3385,6 +3592,7 @@ var LevelProxyHandler = {
3385
3592
  } else if (property === "sublevel") {
3386
3593
  return (...args) => {
3387
3594
  return new Proxy(
3595
+ // eslint-disable-next-line prefer-spread
3388
3596
  target[property].apply(target, args),
3389
3597
  LevelProxyHandler
3390
3598
  );
@@ -3752,6 +3960,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3752
3960
 
3753
3961
  // src/database/datalayer.ts
3754
3962
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
3963
+ var REFS_COLLECTIONS_SORT_KEY = "__refs__";
3964
+ var REFS_REFERENCE_FIELD = "__tina_ref__";
3965
+ var REFS_PATH_FIELD = "__tina_ref_path__";
3755
3966
  var DEFAULT_NUMERIC_LPAD = 4;
3756
3967
  var applyPadding = (input, pad) => {
3757
3968
  if (pad) {
@@ -4261,6 +4472,7 @@ var makeFolderOpsForCollection = (folderTree, collection, indexDefinitions, opTy
4261
4472
  result.push({
4262
4473
  type: opType,
4263
4474
  key: `${collection.path}/${subFolderKey}.${collection.format}`,
4475
+ // replace the root with the collection path
4264
4476
  sublevel: indexSublevel,
4265
4477
  value: {}
4266
4478
  });
@@ -4324,6 +4536,41 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4324
4536
  }
4325
4537
  return result;
4326
4538
  };
4539
+ var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4540
+ const result = [];
4541
+ if (collection) {
4542
+ for (const [c, referencePaths] of Object.entries(references || {})) {
4543
+ if (!referencePaths.length) {
4544
+ continue;
4545
+ }
4546
+ const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4547
+ const refSublevel = collectionSublevel.sublevel(
4548
+ REFS_COLLECTIONS_SORT_KEY,
4549
+ SUBLEVEL_OPTIONS
4550
+ );
4551
+ const references2 = {};
4552
+ for (const path7 of referencePaths) {
4553
+ const ref = JSONPath({ path: path7, json: data });
4554
+ if (references2[ref]) {
4555
+ references2[ref].push(path7);
4556
+ } else {
4557
+ references2[ref] = [path7];
4558
+ }
4559
+ }
4560
+ for (const ref of Object.keys(references2)) {
4561
+ for (const path7 of references2[ref]) {
4562
+ result.push({
4563
+ type: opType,
4564
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4565
+ sublevel: refSublevel,
4566
+ value: opType === "put" ? {} : void 0
4567
+ });
4568
+ }
4569
+ }
4570
+ }
4571
+ }
4572
+ return result;
4573
+ };
4327
4574
  var makeStringEscaper = (regex, replacement) => {
4328
4575
  return (input) => {
4329
4576
  if (Array.isArray(input)) {
@@ -4375,6 +4622,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4375
4622
  case "password":
4376
4623
  accumulator[field.name] = {
4377
4624
  value: void 0,
4625
+ // never resolve the password hash
4378
4626
  passwordChangeRequired: value["passwordChangeRequired"] ?? false
4379
4627
  };
4380
4628
  break;
@@ -4469,7 +4717,7 @@ var resolveFieldData = async ({ namespace, ...field }, rawData, accumulator, tin
4469
4717
  }
4470
4718
  return accumulator;
4471
4719
  };
4472
- var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit) => {
4720
+ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config, isAudit, hasReferences) => {
4473
4721
  const collection = tinaSchema.getCollection(rawData._collection);
4474
4722
  try {
4475
4723
  const template = tinaSchema.getTemplateForData({
@@ -4523,6 +4771,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4523
4771
  basename,
4524
4772
  filename,
4525
4773
  extension,
4774
+ hasReferences,
4526
4775
  path: fullPath,
4527
4776
  relativePath,
4528
4777
  breadcrumbs,
@@ -4542,6 +4791,25 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4542
4791
  throw e;
4543
4792
  }
4544
4793
  };
4794
+ var updateObjectWithJsonPath = (obj, path7, newValue) => {
4795
+ if (!path7.includes(".") && !path7.includes("[")) {
4796
+ if (path7 in obj) {
4797
+ obj[path7] = newValue;
4798
+ }
4799
+ return obj;
4800
+ }
4801
+ const parentPath = path7.replace(/\.[^.]+$/, "");
4802
+ const keyToUpdate = path7.match(/[^.]+$/)[0];
4803
+ const parents = JSONPath2({ path: parentPath, json: obj, resultType: "value" });
4804
+ if (parents.length > 0) {
4805
+ parents.forEach((parent) => {
4806
+ if (parent && typeof parent === "object" && keyToUpdate in parent) {
4807
+ parent[keyToUpdate] = newValue;
4808
+ }
4809
+ });
4810
+ }
4811
+ return obj;
4812
+ };
4545
4813
  var Resolver = class {
4546
4814
  constructor(init) {
4547
4815
  this.init = init;
@@ -4549,6 +4817,7 @@ var Resolver = class {
4549
4817
  const collection = this.tinaSchema.getCollection(collectionName);
4550
4818
  const extraFields = {};
4551
4819
  return {
4820
+ // return the collection and hasDocuments to resolve documents at a lower level
4552
4821
  documents: { collection, hasDocuments },
4553
4822
  ...collection,
4554
4823
  ...extraFields
@@ -4583,17 +4852,19 @@ var Resolver = class {
4583
4852
  );
4584
4853
  }
4585
4854
  };
4586
- this.getDocument = async (fullPath) => {
4855
+ this.getDocument = async (fullPath, opts = {}) => {
4587
4856
  if (typeof fullPath !== "string") {
4588
4857
  throw new Error(`fullPath must be of type string for getDocument request`);
4589
4858
  }
4590
4859
  const rawData = await this.getRaw(fullPath);
4860
+ const hasReferences = opts?.checkReferences ? await this.hasReferences(fullPath, opts.collection) : void 0;
4591
4861
  return transformDocumentIntoPayload(
4592
4862
  fullPath,
4593
4863
  rawData,
4594
4864
  this.tinaSchema,
4595
4865
  this.config,
4596
- this.isAudit
4866
+ this.isAudit,
4867
+ hasReferences
4597
4868
  );
4598
4869
  };
4599
4870
  this.deleteDocument = async (fullPath) => {
@@ -4633,7 +4904,9 @@ var Resolver = class {
4633
4904
  );
4634
4905
  } else {
4635
4906
  return this.buildFieldMutations(
4907
+ // @ts-ignore FIXME Argument of type 'string | object' is not assignable to parameter of type '{ [fieldName: string]: string | object | (string | object)[]; }'
4636
4908
  fieldValue,
4909
+ //@ts-ignore
4637
4910
  objectTemplate,
4638
4911
  existingData
4639
4912
  );
@@ -4645,6 +4918,7 @@ var Resolver = class {
4645
4918
  fieldValue.map(async (item) => {
4646
4919
  if (typeof item === "string") {
4647
4920
  throw new Error(
4921
+ //@ts-ignore
4648
4922
  `Expected object for template value for field ${field.name}`
4649
4923
  );
4650
4924
  }
@@ -4653,16 +4927,19 @@ var Resolver = class {
4653
4927
  });
4654
4928
  const [templateName] = Object.entries(item)[0];
4655
4929
  const template = templates.find(
4930
+ //@ts-ignore
4656
4931
  (template2) => template2.name === templateName
4657
4932
  );
4658
4933
  if (!template) {
4659
4934
  throw new Error(`Expected to find template ${templateName}`);
4660
4935
  }
4661
4936
  return {
4937
+ // @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
4938
  ...await this.buildFieldMutations(
4663
4939
  item[template.name],
4664
4940
  template
4665
4941
  ),
4942
+ //@ts-ignore
4666
4943
  _template: template.name
4667
4944
  };
4668
4945
  })
@@ -4670,6 +4947,7 @@ var Resolver = class {
4670
4947
  } else {
4671
4948
  if (typeof fieldValue === "string") {
4672
4949
  throw new Error(
4950
+ //@ts-ignore
4673
4951
  `Expected object for template value for field ${field.name}`
4674
4952
  );
4675
4953
  }
@@ -4678,16 +4956,19 @@ var Resolver = class {
4678
4956
  });
4679
4957
  const [templateName] = Object.entries(fieldValue)[0];
4680
4958
  const template = templates.find(
4959
+ //@ts-ignore
4681
4960
  (template2) => template2.name === templateName
4682
4961
  );
4683
4962
  if (!template) {
4684
4963
  throw new Error(`Expected to find template ${templateName}`);
4685
4964
  }
4686
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; })[]; }'
4687
4967
  ...await this.buildFieldMutations(
4688
4968
  fieldValue[template.name],
4689
4969
  template
4690
4970
  ),
4971
+ //@ts-ignore
4691
4972
  _template: template.name
4692
4973
  };
4693
4974
  }
@@ -4727,6 +5008,7 @@ var Resolver = class {
4727
5008
  return this.getDocument(realPath);
4728
5009
  }
4729
5010
  const params = await this.buildObjectMutations(
5011
+ // @ts-ignore
4730
5012
  args.params[collection.name],
4731
5013
  collection
4732
5014
  );
@@ -4772,6 +5054,7 @@ var Resolver = class {
4772
5054
  const values = {
4773
5055
  ...oldDoc,
4774
5056
  ...await this.buildFieldMutations(
5057
+ // @ts-ignore FIXME: failing on unknown, which we don't need to know because it's recursive
4775
5058
  templateParams,
4776
5059
  template,
4777
5060
  doc?._rawData
@@ -4785,6 +5068,7 @@ var Resolver = class {
4785
5068
  return this.getDocument(realPath);
4786
5069
  }
4787
5070
  const params = await this.buildObjectMutations(
5071
+ //@ts-ignore
4788
5072
  isCollectionSpecific ? args.params : args.params[collection.name],
4789
5073
  collection,
4790
5074
  doc?._rawData
@@ -4792,6 +5076,10 @@ var Resolver = class {
4792
5076
  await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
4793
5077
  return this.getDocument(realPath);
4794
5078
  };
5079
+ /**
5080
+ * Returns top-level fields which are not defined in the collection, so their
5081
+ * values are not eliminated from Tina when new values are saved
5082
+ */
4795
5083
  this.resolveLegacyValues = (oldDoc, collection) => {
4796
5084
  const legacyValues = {};
4797
5085
  Object.entries(oldDoc).forEach(([key, value]) => {
@@ -4896,6 +5184,20 @@ var Resolver = class {
4896
5184
  if (isDeletion) {
4897
5185
  const doc = await this.getDocument(realPath);
4898
5186
  await this.deleteDocument(realPath);
5187
+ if (await this.hasReferences(realPath, collection)) {
5188
+ const collRefs = await this.findReferences(realPath, collection);
5189
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5190
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5191
+ docsWithRefs
5192
+ )) {
5193
+ let refDoc = await this.getRaw(pathToDocWithRef);
5194
+ for (const path7 of referencePaths) {
5195
+ refDoc = updateObjectWithJsonPath(refDoc, path7, null);
5196
+ }
5197
+ await this.database.put(pathToDocWithRef, refDoc, collection2);
5198
+ }
5199
+ }
5200
+ }
4899
5201
  return doc;
4900
5202
  }
4901
5203
  if (isUpdateName) {
@@ -4914,6 +5216,22 @@ var Resolver = class {
4914
5216
  );
4915
5217
  await this.database.put(newRealPath, doc._rawData, collection.name);
4916
5218
  await this.deleteDocument(realPath);
5219
+ const collRefs = await this.findReferences(realPath, collection);
5220
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5221
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5222
+ docsWithRefs
5223
+ )) {
5224
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5225
+ for (const path7 of referencePaths) {
5226
+ docWithRef = updateObjectWithJsonPath(
5227
+ docWithRef,
5228
+ path7,
5229
+ newRealPath
5230
+ );
5231
+ }
5232
+ await this.database.put(pathToDocWithRef, docWithRef, collection2);
5233
+ }
5234
+ }
4917
5235
  return this.getDocument(newRealPath);
4918
5236
  }
4919
5237
  if (alreadyExists === false) {
@@ -4927,7 +5245,10 @@ var Resolver = class {
4927
5245
  isCollectionSpecific
4928
5246
  });
4929
5247
  } else {
4930
- return this.getDocument(realPath);
5248
+ return this.getDocument(realPath, {
5249
+ collection,
5250
+ checkReferences: true
5251
+ });
4931
5252
  }
4932
5253
  };
4933
5254
  this.resolveCollectionConnections = async ({ ids }) => {
@@ -4964,6 +5285,7 @@ var Resolver = class {
4964
5285
  },
4965
5286
  collection: referencedCollection,
4966
5287
  hydrator: (path7) => path7
5288
+ // just return the path
4967
5289
  }
4968
5290
  );
4969
5291
  const { edges } = resolvedCollectionConnection;
@@ -5031,6 +5353,82 @@ var Resolver = class {
5031
5353
  }
5032
5354
  };
5033
5355
  };
5356
+ /**
5357
+ * Checks if a document has references to it
5358
+ * @param id The id of the document to check for references
5359
+ * @param c The collection to check for references
5360
+ * @returns true if the document has references, false otherwise
5361
+ */
5362
+ this.hasReferences = async (id, c) => {
5363
+ let count = 0;
5364
+ await this.database.query(
5365
+ {
5366
+ collection: c.name,
5367
+ filterChain: makeFilterChain({
5368
+ conditions: [
5369
+ {
5370
+ filterPath: REFS_REFERENCE_FIELD,
5371
+ filterExpression: {
5372
+ _type: "string",
5373
+ _list: false,
5374
+ eq: id
5375
+ }
5376
+ }
5377
+ ]
5378
+ }),
5379
+ sort: REFS_COLLECTIONS_SORT_KEY
5380
+ },
5381
+ (refId) => {
5382
+ count++;
5383
+ return refId;
5384
+ }
5385
+ );
5386
+ if (count) {
5387
+ return true;
5388
+ }
5389
+ return false;
5390
+ };
5391
+ /**
5392
+ * Finds references to a document
5393
+ * @param id the id of the document to find references to
5394
+ * @param c the collection to find references in
5395
+ * @returns a map of references to the document
5396
+ */
5397
+ this.findReferences = async (id, c) => {
5398
+ const references = {};
5399
+ await this.database.query(
5400
+ {
5401
+ collection: c.name,
5402
+ filterChain: makeFilterChain({
5403
+ conditions: [
5404
+ {
5405
+ filterPath: REFS_REFERENCE_FIELD,
5406
+ filterExpression: {
5407
+ _type: "string",
5408
+ _list: false,
5409
+ eq: id
5410
+ }
5411
+ }
5412
+ ]
5413
+ }),
5414
+ sort: REFS_COLLECTIONS_SORT_KEY
5415
+ },
5416
+ (refId, rawItem) => {
5417
+ if (!references[c.name]) {
5418
+ references[c.name] = {};
5419
+ }
5420
+ if (!references[c.name][refId]) {
5421
+ references[c.name][refId] = [];
5422
+ }
5423
+ const referencePath = rawItem?.[REFS_PATH_FIELD];
5424
+ if (referencePath) {
5425
+ references[c.name][refId].push(referencePath);
5426
+ }
5427
+ return refId;
5428
+ }
5429
+ );
5430
+ return references;
5431
+ };
5034
5432
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
5035
5433
  const accum = {};
5036
5434
  for (const passwordField of template.fields.filter(
@@ -5117,6 +5515,27 @@ var Resolver = class {
5117
5515
  }
5118
5516
  return accum;
5119
5517
  };
5518
+ /**
5519
+ * A mutation looks nearly identical between updateDocument:
5520
+ * ```graphql
5521
+ * updateDocument(collection: $collection,relativePath: $path, params: {
5522
+ * post: {
5523
+ * title: "Hello, World"
5524
+ * }
5525
+ * })`
5526
+ * ```
5527
+ * and `updatePostDocument`:
5528
+ * ```graphql
5529
+ * updatePostDocument(relativePath: $path, params: {
5530
+ * title: "Hello, World"
5531
+ * })
5532
+ * ```
5533
+ * The problem here is that we don't know whether the payload came from `updateDocument`
5534
+ * or `updatePostDocument` (we could, but for now it's easier not to pipe those details through),
5535
+ * But we do know that when given a `args.collection` value, we can assume that
5536
+ * this was a `updateDocument` request, and thus - should grab the data
5537
+ * from the corresponding field name in the key
5538
+ */
5120
5539
  this.buildParams = (args) => {
5121
5540
  try {
5122
5541
  assertShape(
@@ -5216,7 +5635,10 @@ var resolve = async ({
5216
5635
  const graphQLSchema = buildASTSchema(graphQLSchemaAst);
5217
5636
  const tinaConfig = await database.getTinaSchema();
5218
5637
  const tinaSchema = await createSchema({
5638
+ // TODO: please update all the types to import from @tinacms/schema-tools
5639
+ // @ts-ignore
5219
5640
  schema: tinaConfig,
5641
+ // @ts-ignore
5220
5642
  flags: tinaConfig?.meta?.flags
5221
5643
  });
5222
5644
  const resolver = createResolver({
@@ -5233,8 +5655,7 @@ var resolve = async ({
5233
5655
  database
5234
5656
  },
5235
5657
  typeResolver: async (source, _args, info) => {
5236
- if (source.__typename)
5237
- return source.__typename;
5658
+ if (source.__typename) return source.__typename;
5238
5659
  const namedType = getNamedType(info.returnType).toString();
5239
5660
  const lookup = await database.getLookup(namedType);
5240
5661
  if (lookup.resolveType === "unionData") {
@@ -5383,11 +5804,13 @@ var resolve = async ({
5383
5804
  set(
5384
5805
  params,
5385
5806
  userField.path.slice(1),
5807
+ // remove _rawData from users path
5386
5808
  users.map((u) => {
5387
5809
  if (user[idFieldName] === u[idFieldName]) {
5388
5810
  return user;
5389
5811
  }
5390
5812
  return {
5813
+ // don't overwrite other users' passwords
5391
5814
  ...u,
5392
5815
  [passwordFieldName]: {
5393
5816
  ...u[passwordFieldName],
@@ -5410,6 +5833,9 @@ var resolve = async ({
5410
5833
  }
5411
5834
  const isCreation = lookup[info.fieldName] === "create";
5412
5835
  switch (lookup.resolveType) {
5836
+ /**
5837
+ * `node(id: $id)`
5838
+ */
5413
5839
  case "nodeDocument":
5414
5840
  assertShape(
5415
5841
  args,
@@ -5441,6 +5867,7 @@ var resolve = async ({
5441
5867
  collection: args.collection,
5442
5868
  isMutation,
5443
5869
  isCreation,
5870
+ // Right now this is the only case for deletion
5444
5871
  isDeletion: info.fieldName === "deleteDocument",
5445
5872
  isFolderCreation: info.fieldName === "createFolder",
5446
5873
  isUpdateName: Boolean(args?.params?.relativePath),
@@ -5450,6 +5877,9 @@ var resolve = async ({
5450
5877
  return result;
5451
5878
  }
5452
5879
  return value;
5880
+ /**
5881
+ * eg `getMovieDocument.data.actors`
5882
+ */
5453
5883
  case "multiCollectionDocumentList":
5454
5884
  if (Array.isArray(value)) {
5455
5885
  return {
@@ -5461,7 +5891,15 @@ var resolve = async ({
5461
5891
  }
5462
5892
  if (info.fieldName === "documents" && value?.collection && value?.hasDocuments) {
5463
5893
  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") {
5894
+ if (
5895
+ // 1. Make sure that the filter exists
5896
+ typeof args?.filter !== "undefined" && args?.filter !== null && // 2. Make sure that the collection name exists
5897
+ // @ts-ignore
5898
+ typeof value?.collection?.name === "string" && // 3. Make sure that the collection name is in the filter and is not undefined
5899
+ // @ts-ignore
5900
+ Object.keys(args.filter).includes(value?.collection?.name) && // @ts-ignore
5901
+ typeof args.filter[value?.collection?.name] !== "undefined"
5902
+ ) {
5465
5903
  filter = args.filter[value.collection.name];
5466
5904
  }
5467
5905
  return resolver.resolveCollectionConnection({
@@ -5469,12 +5907,20 @@ var resolve = async ({
5469
5907
  ...args,
5470
5908
  filter
5471
5909
  },
5910
+ // @ts-ignore
5472
5911
  collection: value.collection
5473
5912
  });
5474
5913
  }
5475
5914
  throw new Error(
5476
5915
  `Expected an array for result of ${info.fieldName} at ${info.path}`
5477
5916
  );
5917
+ /**
5918
+ * Collections-specific getter
5919
+ * eg. `getPostDocument`/`createPostDocument`/`updatePostDocument`
5920
+ *
5921
+ * if coming from a query result
5922
+ * the field will be `node`
5923
+ */
5478
5924
  case "collectionDocument": {
5479
5925
  if (value) {
5480
5926
  return value;
@@ -5489,11 +5935,32 @@ var resolve = async ({
5489
5935
  });
5490
5936
  return result;
5491
5937
  }
5938
+ /**
5939
+ * Collections-specific list getter
5940
+ * eg. `getPageList`
5941
+ */
5492
5942
  case "collectionDocumentList":
5493
5943
  return resolver.resolveCollectionConnection({
5494
5944
  args,
5495
5945
  collection: tinaSchema.getCollection(lookup.collection)
5496
5946
  });
5947
+ /**
5948
+ * A polymorphic data set, it can be from a document's data
5949
+ * of any nested object which can be one of many shapes
5950
+ *
5951
+ * ```graphql
5952
+ * getPostDocument(relativePath: $relativePath) {
5953
+ * data {...} <- this part
5954
+ * }
5955
+ * ```
5956
+ * ```graphql
5957
+ * getBlockDocument(relativePath: $relativePath) {
5958
+ * data {
5959
+ * blocks {...} <- or this part
5960
+ * }
5961
+ * }
5962
+ * ```
5963
+ */
5497
5964
  case "unionData":
5498
5965
  if (!value) {
5499
5966
  if (args.relativePath) {
@@ -5558,8 +6025,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
5558
6025
  this.port = port || 9e3;
5559
6026
  }
5560
6027
  openConnection() {
5561
- if (this._connected)
5562
- return;
6028
+ if (this._connected) return;
5563
6029
  const socket = connect(this.port);
5564
6030
  pipeline(socket, this.createRpcStream(), socket, () => {
5565
6031
  this._connected = false;
@@ -5569,7 +6035,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
5569
6035
  };
5570
6036
 
5571
6037
  // src/database/index.ts
5572
- import path4 from "path";
6038
+ import path4 from "node:path";
5573
6039
  import { GraphQLError as GraphQLError5 } from "graphql";
5574
6040
  import micromatch2 from "micromatch";
5575
6041
  import sha2 from "js-sha1";
@@ -5704,6 +6170,7 @@ var Database = class {
5704
6170
  );
5705
6171
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
5706
6172
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6173
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
5707
6174
  const normalizedPath = normalizePath(filepath);
5708
6175
  if (!collection?.isDetached) {
5709
6176
  if (this.bridge) {
@@ -5732,6 +6199,14 @@ var Database = class {
5732
6199
  let delOps = [];
5733
6200
  if (!isGitKeep(normalizedPath, collection)) {
5734
6201
  putOps = [
6202
+ ...makeRefOpsForDocument(
6203
+ normalizedPath,
6204
+ collection?.name,
6205
+ collectionReferences,
6206
+ dataFields,
6207
+ "put",
6208
+ level
6209
+ ),
5735
6210
  ...makeIndexOpsForDocument(
5736
6211
  normalizedPath,
5737
6212
  collection?.name,
@@ -5740,6 +6215,7 @@ var Database = class {
5740
6215
  "put",
5741
6216
  level
5742
6217
  ),
6218
+ // folder indices
5743
6219
  ...makeIndexOpsForDocument(
5744
6220
  normalizedPath,
5745
6221
  `${collection?.name}_${folderKey}`,
@@ -5754,6 +6230,14 @@ var Database = class {
5754
6230
  SUBLEVEL_OPTIONS
5755
6231
  ).get(normalizedPath);
5756
6232
  delOps = existingItem ? [
6233
+ ...makeRefOpsForDocument(
6234
+ normalizedPath,
6235
+ collection?.name,
6236
+ collectionReferences,
6237
+ existingItem,
6238
+ "del",
6239
+ level
6240
+ ),
5757
6241
  ...makeIndexOpsForDocument(
5758
6242
  normalizedPath,
5759
6243
  collection?.name,
@@ -5762,6 +6246,7 @@ var Database = class {
5762
6246
  "del",
5763
6247
  level
5764
6248
  ),
6249
+ // folder indices
5765
6250
  ...makeIndexOpsForDocument(
5766
6251
  normalizedPath,
5767
6252
  `${collection?.name}_${folderKey}`,
@@ -5800,6 +6285,7 @@ var Database = class {
5800
6285
  );
5801
6286
  collectionIndexDefinitions = indexDefinitions?.[collectionName];
5802
6287
  }
6288
+ const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
5803
6289
  const normalizedPath = normalizePath(filepath);
5804
6290
  const dataFields = await this.formatBodyOnPayload(filepath, data);
5805
6291
  const collection = await this.collectionForPath(filepath);
@@ -5847,6 +6333,14 @@ var Database = class {
5847
6333
  let delOps = [];
5848
6334
  if (!isGitKeep(normalizedPath, collection)) {
5849
6335
  putOps = [
6336
+ ...makeRefOpsForDocument(
6337
+ normalizedPath,
6338
+ collectionName,
6339
+ collectionReferences,
6340
+ dataFields,
6341
+ "put",
6342
+ level
6343
+ ),
5850
6344
  ...makeIndexOpsForDocument(
5851
6345
  normalizedPath,
5852
6346
  collectionName,
@@ -5855,6 +6349,7 @@ var Database = class {
5855
6349
  "put",
5856
6350
  level
5857
6351
  ),
6352
+ // folder indices
5858
6353
  ...makeIndexOpsForDocument(
5859
6354
  normalizedPath,
5860
6355
  `${collection?.name}_${folderKey}`,
@@ -5869,6 +6364,14 @@ var Database = class {
5869
6364
  SUBLEVEL_OPTIONS
5870
6365
  ).get(normalizedPath);
5871
6366
  delOps = existingItem ? [
6367
+ ...makeRefOpsForDocument(
6368
+ normalizedPath,
6369
+ collectionName,
6370
+ collectionReferences,
6371
+ existingItem,
6372
+ "del",
6373
+ level
6374
+ ),
5872
6375
  ...makeIndexOpsForDocument(
5873
6376
  normalizedPath,
5874
6377
  collectionName,
@@ -5877,6 +6380,7 @@ var Database = class {
5877
6380
  "del",
5878
6381
  level
5879
6382
  ),
6383
+ // folder indices
5880
6384
  ...makeIndexOpsForDocument(
5881
6385
  normalizedPath,
5882
6386
  `${collection?.name}_${folderKey}`,
@@ -5954,6 +6458,7 @@ var Database = class {
5954
6458
  aliasedData,
5955
6459
  extension,
5956
6460
  writeTemplateKey,
6461
+ //templateInfo.type === 'union',
5957
6462
  {
5958
6463
  frontmatterFormat: collection?.frontmatterFormat,
5959
6464
  frontmatterDelimiters: collection?.frontmatterDelimiters
@@ -5992,6 +6497,7 @@ var Database = class {
5992
6497
  SUBLEVEL_OPTIONS
5993
6498
  ).get(graphqlPath);
5994
6499
  };
6500
+ //TODO - is there a reason why the database fetches some config with "bridge.get", and some with "store.get"?
5995
6501
  this.getGraphQLSchemaFromBridge = async () => {
5996
6502
  if (!this.bridge) {
5997
6503
  throw new Error(`No bridge configured`);
@@ -6028,6 +6534,22 @@ var Database = class {
6028
6534
  this.tinaSchema = await createSchema({ schema });
6029
6535
  return this.tinaSchema;
6030
6536
  };
6537
+ this.getCollectionReferences = async (level) => {
6538
+ if (this.collectionReferences) {
6539
+ return this.collectionReferences;
6540
+ }
6541
+ const result = {};
6542
+ const schema = await this.getSchema(level || this.contentLevel);
6543
+ const collections = schema.getCollections();
6544
+ for (const collection of collections) {
6545
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6546
+ collection.name
6547
+ );
6548
+ result[collection.name] = collectionReferences;
6549
+ }
6550
+ this.collectionReferences = result;
6551
+ return result;
6552
+ };
6031
6553
  this.getIndexDefinitions = async (level) => {
6032
6554
  if (!this.collectionIndexDefinitions) {
6033
6555
  await new Promise(async (resolve2, reject) => {
@@ -6037,7 +6559,23 @@ var Database = class {
6037
6559
  const collections = schema.getCollections();
6038
6560
  for (const collection of collections) {
6039
6561
  const indexDefinitions = {
6040
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6562
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6563
+ // provide a default sort key which is the file sort
6564
+ // pseudo-index for the collection's references
6565
+ [REFS_COLLECTIONS_SORT_KEY]: {
6566
+ fields: [
6567
+ {
6568
+ name: REFS_REFERENCE_FIELD,
6569
+ type: "string",
6570
+ list: false
6571
+ },
6572
+ {
6573
+ name: REFS_PATH_FIELD,
6574
+ type: "string",
6575
+ list: false
6576
+ }
6577
+ ]
6578
+ }
6041
6579
  };
6042
6580
  if (collection.fields) {
6043
6581
  for (const field of collection.fields) {
@@ -6188,29 +6726,36 @@ var Database = class {
6188
6726
  }
6189
6727
  startKey = startKey || key || "";
6190
6728
  endKey = key || "";
6191
- edges = [...edges, { cursor: key, path: filepath }];
6729
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6192
6730
  }
6193
6731
  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
- });
6732
+ edges: await sequential(
6733
+ edges,
6734
+ async ({
6735
+ cursor,
6736
+ path: path7,
6737
+ value
6738
+ }) => {
6739
+ try {
6740
+ const node = await hydrator(path7, value);
6741
+ return {
6742
+ node,
6743
+ cursor: btoa(cursor)
6744
+ };
6745
+ } catch (error) {
6746
+ console.log(error);
6747
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6748
+ throw new TinaQueryError({
6749
+ originalError: error,
6750
+ file: path7,
6751
+ collection: collection.name,
6752
+ stack: error.stack
6753
+ });
6754
+ }
6755
+ throw error;
6210
6756
  }
6211
- throw error;
6212
6757
  }
6213
- }),
6758
+ ),
6214
6759
  pageInfo: {
6215
6760
  hasPreviousPage,
6216
6761
  hasNextPage,
@@ -6356,17 +6901,18 @@ var Database = class {
6356
6901
  throw new Error(`No collection found for path: ${filepath}`);
6357
6902
  }
6358
6903
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
6904
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6359
6905
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6360
6906
  let level = this.contentLevel;
6361
6907
  if (collection?.isDetached) {
6362
6908
  level = this.appLevel.sublevel(collection?.name, SUBLEVEL_OPTIONS);
6363
6909
  }
6364
- const itemKey = normalizePath(filepath);
6910
+ const normalizedPath = normalizePath(filepath);
6365
6911
  const rootSublevel = level.sublevel(
6366
6912
  CONTENT_ROOT_PREFIX,
6367
6913
  SUBLEVEL_OPTIONS
6368
6914
  );
6369
- const item = await rootSublevel.get(itemKey);
6915
+ const item = await rootSublevel.get(normalizedPath);
6370
6916
  if (item) {
6371
6917
  const folderTreeBuilder = new FolderTreeBuilder();
6372
6918
  const folderKey = folderTreeBuilder.update(
@@ -6374,16 +6920,25 @@ var Database = class {
6374
6920
  collection.path || ""
6375
6921
  );
6376
6922
  await this.contentLevel.batch([
6923
+ ...makeRefOpsForDocument(
6924
+ normalizedPath,
6925
+ collection.name,
6926
+ collectionReferences,
6927
+ item,
6928
+ "del",
6929
+ level
6930
+ ),
6377
6931
  ...makeIndexOpsForDocument(
6378
- filepath,
6932
+ normalizedPath,
6379
6933
  collection.name,
6380
6934
  collectionIndexDefinitions,
6381
6935
  item,
6382
6936
  "del",
6383
6937
  level
6384
6938
  ),
6939
+ // folder indices
6385
6940
  ...makeIndexOpsForDocument(
6386
- filepath,
6941
+ normalizedPath,
6387
6942
  `${collection.name}_${folderKey}`,
6388
6943
  collectionIndexDefinitions,
6389
6944
  item,
@@ -6392,17 +6947,17 @@ var Database = class {
6392
6947
  ),
6393
6948
  {
6394
6949
  type: "del",
6395
- key: itemKey,
6950
+ key: normalizedPath,
6396
6951
  sublevel: rootSublevel
6397
6952
  }
6398
6953
  ]);
6399
6954
  }
6400
6955
  if (!collection?.isDetached) {
6401
6956
  if (this.bridge) {
6402
- await this.bridge.delete(normalizePath(filepath));
6957
+ await this.bridge.delete(normalizedPath);
6403
6958
  }
6404
6959
  try {
6405
- await this.onDelete(normalizePath(filepath));
6960
+ await this.onDelete(normalizedPath);
6406
6961
  } catch (e) {
6407
6962
  throw new GraphQLError5(
6408
6963
  `Error running onDelete hook for ${filepath}: ${e}`,
@@ -6536,6 +7091,9 @@ var Database = class {
6536
7091
  info: templateInfo
6537
7092
  };
6538
7093
  }
7094
+ /**
7095
+ * Clears the internal cache of the tinaSchema and the lookup file. This allows the state to be reset
7096
+ */
6539
7097
  clearCache() {
6540
7098
  this.tinaSchema = null;
6541
7099
  this._lookup = null;
@@ -6597,6 +7155,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6597
7155
  }
6598
7156
  collectionPath = collection.path;
6599
7157
  }
7158
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
6600
7159
  const tinaSchema = await database.getSchema();
6601
7160
  let templateInfo = null;
6602
7161
  if (collection) {
@@ -6618,12 +7177,59 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6618
7177
  await hashPasswordValues(aliasedData, passwordFields);
6619
7178
  }
6620
7179
  const normalizedPath = normalizePath(filepath);
7180
+ const rootSublevel = level.sublevel(
7181
+ CONTENT_ROOT_PREFIX,
7182
+ SUBLEVEL_OPTIONS
7183
+ );
6621
7184
  const folderKey = folderTreeBuilder.update(
6622
7185
  normalizedPath,
6623
7186
  collectionPath || ""
6624
7187
  );
7188
+ const item = await rootSublevel.get(normalizedPath);
7189
+ if (item) {
7190
+ await database.contentLevel.batch([
7191
+ ...makeRefOpsForDocument(
7192
+ normalizedPath,
7193
+ collection?.name,
7194
+ collectionReferences,
7195
+ item,
7196
+ "del",
7197
+ level
7198
+ ),
7199
+ ...makeIndexOpsForDocument(
7200
+ normalizedPath,
7201
+ collection.name,
7202
+ collectionIndexDefinitions,
7203
+ item,
7204
+ "del",
7205
+ level
7206
+ ),
7207
+ // folder indices
7208
+ ...makeIndexOpsForDocument(
7209
+ normalizedPath,
7210
+ `${collection.name}_${folderKey}`,
7211
+ collectionIndexDefinitions,
7212
+ item,
7213
+ "del",
7214
+ level
7215
+ ),
7216
+ {
7217
+ type: "del",
7218
+ key: normalizedPath,
7219
+ sublevel: rootSublevel
7220
+ }
7221
+ ]);
7222
+ }
6625
7223
  if (!isGitKeep(filepath, collection)) {
6626
7224
  await enqueueOps([
7225
+ ...makeRefOpsForDocument(
7226
+ normalizedPath,
7227
+ collection?.name,
7228
+ collectionReferences,
7229
+ aliasedData,
7230
+ "put",
7231
+ level
7232
+ ),
6627
7233
  ...makeIndexOpsForDocument(
6628
7234
  normalizedPath,
6629
7235
  collection?.name,
@@ -6632,6 +7238,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
6632
7238
  "put",
6633
7239
  level
6634
7240
  ),
7241
+ // folder indexes
6635
7242
  ...makeIndexOpsForDocument(
6636
7243
  normalizedPath,
6637
7244
  `${collection?.name}_${folderKey}`,
@@ -6686,6 +7293,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6686
7293
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
6687
7294
  }
6688
7295
  }
7296
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
6689
7297
  const tinaSchema = await database.getSchema();
6690
7298
  let templateInfo = null;
6691
7299
  if (collection) {
@@ -6709,6 +7317,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6709
7317
  item
6710
7318
  ) : item;
6711
7319
  await enqueueOps([
7320
+ ...makeRefOpsForDocument(
7321
+ itemKey,
7322
+ collection?.name,
7323
+ collectionReferences,
7324
+ aliasedData,
7325
+ "del",
7326
+ database.contentLevel
7327
+ ),
6712
7328
  ...makeIndexOpsForDocument(
6713
7329
  itemKey,
6714
7330
  collection.name,
@@ -6717,6 +7333,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
6717
7333
  "del",
6718
7334
  database.contentLevel
6719
7335
  ),
7336
+ // folder indexes
6720
7337
  ...makeIndexOpsForDocument(
6721
7338
  itemKey,
6722
7339
  `${collection?.name}_${folderKey}`,
@@ -6932,17 +7549,26 @@ var IsomorphicBridge = class {
6932
7549
  getAuthor() {
6933
7550
  return {
6934
7551
  ...this.author,
6935
- timestamp: Math.round(new Date().getTime() / 1e3),
7552
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
6936
7553
  timezoneOffset: 0
6937
7554
  };
6938
7555
  }
6939
7556
  getCommitter() {
6940
7557
  return {
6941
7558
  ...this.committer,
6942
- timestamp: Math.round(new Date().getTime() / 1e3),
7559
+ timestamp: Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3),
6943
7560
  timezoneOffset: 0
6944
7561
  };
6945
7562
  }
7563
+ /**
7564
+ * Recursively populate paths matching `pattern` for the given `entry`
7565
+ *
7566
+ * @param pattern - pattern to filter paths by
7567
+ * @param entry - TreeEntry to start building list from
7568
+ * @param path - base path
7569
+ * @param results
7570
+ * @private
7571
+ */
6946
7572
  async listEntries({
6947
7573
  pattern,
6948
7574
  entry,
@@ -6975,6 +7601,15 @@ var IsomorphicBridge = class {
6975
7601
  });
6976
7602
  }
6977
7603
  }
7604
+ /**
7605
+ * For the specified path, returns an object with an array containing the parts of the path (pathParts)
7606
+ * and an array containing the WalkerEntry objects for the path parts (pathEntries). Any null elements in the
7607
+ * pathEntries are placeholders for non-existent entries.
7608
+ *
7609
+ * @param path - path being resolved
7610
+ * @param ref - ref to resolve path entries for
7611
+ * @private
7612
+ */
6978
7613
  async resolvePathEntries(path7, ref) {
6979
7614
  let pathParts = path7.split("/");
6980
7615
  const result = await git2.walk({
@@ -7005,6 +7640,17 @@ var IsomorphicBridge = class {
7005
7640
  }
7006
7641
  return { pathParts, pathEntries };
7007
7642
  }
7643
+ /**
7644
+ * Updates tree entry and associated parent tree entries
7645
+ *
7646
+ * @param existingOid - the existing OID
7647
+ * @param updatedOid - the updated OID
7648
+ * @param path - the path of the entry being updated
7649
+ * @param type - the type of the entry being updated (blob or tree)
7650
+ * @param pathEntries - parent path entries
7651
+ * @param pathParts - parent path parts
7652
+ * @private
7653
+ */
7008
7654
  async updateTreeHierarchy(existingOid, updatedOid, path7, type, pathEntries, pathParts) {
7009
7655
  const lastIdx = pathEntries.length - 1;
7010
7656
  const parentEntry = pathEntries[lastIdx];
@@ -7060,6 +7706,13 @@ var IsomorphicBridge = class {
7060
7706
  );
7061
7707
  }
7062
7708
  }
7709
+ /**
7710
+ * Creates a commit for the specified tree and updates the specified ref to point to the commit
7711
+ *
7712
+ * @param treeSha - sha of the new tree
7713
+ * @param ref - the ref that should be updated
7714
+ * @private
7715
+ */
7063
7716
  async commitTree(treeSha, ref) {
7064
7717
  const commitSha = await git2.writeCommit({
7065
7718
  ...this.isomorphicConfig,
@@ -7072,6 +7725,7 @@ var IsomorphicBridge = class {
7072
7725
  })
7073
7726
  ],
7074
7727
  message: this.commitMessage,
7728
+ // TODO these should be configurable
7075
7729
  author: this.getAuthor(),
7076
7730
  committer: this.getCommitter()
7077
7731
  }
@@ -7309,5 +7963,5 @@ export {
7309
7963
  transformDocument,
7310
7964
  transformDocumentIntoPayload
7311
7965
  };
7312
- //! Replaces _.flattenDeep()
7313
7966
  //! Replaces _.get()
7967
+ //! Replaces _.flattenDeep()