@tinacms/graphql 1.4.7 → 1.4.8

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.es.js CHANGED
@@ -459,6 +459,7 @@ var astBuilder = {
459
459
  },
460
460
  MultiCollectionDocument: "DocumentNode",
461
461
  CollectionDocumentUnion: "DocumentUnion",
462
+ Folder: "Folder",
462
463
  String: "String",
463
464
  Reference: "Reference",
464
465
  Collection: "Collection",
@@ -1227,6 +1228,21 @@ var scalarDefinitions = [
1227
1228
  })
1228
1229
  ]
1229
1230
  }),
1231
+ astBuilder.ObjectTypeDefinition({
1232
+ name: astBuilder.TYPES.Folder,
1233
+ fields: [
1234
+ astBuilder.FieldDefinition({
1235
+ name: "name",
1236
+ required: true,
1237
+ type: astBuilder.TYPES.String
1238
+ }),
1239
+ astBuilder.FieldDefinition({
1240
+ name: "path",
1241
+ required: true,
1242
+ type: astBuilder.TYPES.String
1243
+ })
1244
+ ]
1245
+ }),
1230
1246
  astBuilder.ObjectTypeDefinition({
1231
1247
  name: "PageInfo",
1232
1248
  fields: [
@@ -1279,7 +1295,8 @@ var Builder = class {
1279
1295
  namespace: ["document"],
1280
1296
  nodeType: astBuilder.TYPES.MultiCollectionDocument,
1281
1297
  collections,
1282
- connectionNamespace: ["document"]
1298
+ connectionNamespace: ["document"],
1299
+ includeFolderFilter: true
1283
1300
  });
1284
1301
  const type = astBuilder.ObjectTypeDefinition({
1285
1302
  name: typeName,
@@ -1378,7 +1395,8 @@ var Builder = class {
1378
1395
  ];
1379
1396
  const type = await this._buildMultiCollectionDocumentDefinition({
1380
1397
  fieldName: astBuilder.TYPES.MultiCollectionDocument,
1381
- collections
1398
+ collections,
1399
+ includeFolderType: true
1382
1400
  });
1383
1401
  return astBuilder.FieldDefinition({
1384
1402
  name,
@@ -1807,7 +1825,8 @@ var Builder = class {
1807
1825
  };
1808
1826
  this._buildMultiCollectionDocumentDefinition = async ({
1809
1827
  fieldName,
1810
- collections
1828
+ collections,
1829
+ includeFolderType
1811
1830
  }) => {
1812
1831
  const types = [];
1813
1832
  collections.forEach((collection) => {
@@ -1817,14 +1836,14 @@ var Builder = class {
1817
1836
  }
1818
1837
  if (collection.templates) {
1819
1838
  collection.templates.forEach((template) => {
1820
- if (typeof template === "string") {
1821
- throw new Error("Global templates not yet supported");
1822
- }
1823
1839
  const typeName = NAMER.documentTypeName(template.namespace);
1824
1840
  types.push(typeName);
1825
1841
  });
1826
1842
  }
1827
1843
  });
1844
+ if (includeFolderType) {
1845
+ types.push(astBuilder.TYPES.Folder);
1846
+ }
1828
1847
  const type = astBuilder.UnionTypeDefinition({
1829
1848
  name: fieldName,
1830
1849
  types
@@ -1842,7 +1861,8 @@ var Builder = class {
1842
1861
  namespace,
1843
1862
  nodeType,
1844
1863
  collections,
1845
- connectionNamespace
1864
+ connectionNamespace,
1865
+ includeFolderFilter
1846
1866
  }) => {
1847
1867
  const connectionName = NAMER.referenceConnectionType(namespace);
1848
1868
  await this.database.addToLookupMap({
@@ -1855,7 +1875,8 @@ var Builder = class {
1855
1875
  namespace: connectionNamespace,
1856
1876
  connectionName,
1857
1877
  nodeType,
1858
- collections
1878
+ collections,
1879
+ includeFolderFilter
1859
1880
  });
1860
1881
  };
1861
1882
  this._buildFieldFilter = async (field) => {
@@ -2162,7 +2183,8 @@ var Builder = class {
2162
2183
  connectionName,
2163
2184
  nodeType,
2164
2185
  collection,
2165
- collections
2186
+ collections,
2187
+ includeFolderFilter
2166
2188
  }) => {
2167
2189
  const extra = [
2168
2190
  await this._connectionFilterBuilder({
@@ -2172,6 +2194,14 @@ var Builder = class {
2172
2194
  collections
2173
2195
  })
2174
2196
  ];
2197
+ if (includeFolderFilter) {
2198
+ extra.push(
2199
+ astBuilder.InputValueDefinition({
2200
+ name: "folder",
2201
+ type: astBuilder.TYPES.String
2202
+ })
2203
+ );
2204
+ }
2175
2205
  return astBuilder.FieldDefinition({
2176
2206
  name: fieldName,
2177
2207
  required: true,
@@ -2436,7 +2466,12 @@ var validateCollection = async (collection) => {
2436
2466
  name: yup2.string().matches(/^[a-zA-Z0-9_]*$/, {
2437
2467
  message: (obj) => `Collection's "name" must match ${obj.regex} at ${messageName}`
2438
2468
  }).required(),
2439
- path: yup2.string().required().transform((value) => {
2469
+ path: yup2.string().test("is-required", "path is a required field", (value) => {
2470
+ if (value === "") {
2471
+ return true;
2472
+ }
2473
+ return yup2.string().required().isValidSync(value);
2474
+ }).transform((value) => {
2440
2475
  return value.replace(/^\/|\/$/g, "");
2441
2476
  })
2442
2477
  });
@@ -2491,7 +2526,7 @@ var validateField = async (field) => {
2491
2526
  // package.json
2492
2527
  var package_default = {
2493
2528
  name: "@tinacms/graphql",
2494
- version: "1.4.7",
2529
+ version: "1.4.8",
2495
2530
  main: "dist/index.js",
2496
2531
  module: "dist/index.es.js",
2497
2532
  typings: "dist/index.d.ts",
@@ -2543,6 +2578,7 @@ var package_default = {
2543
2578
  "graphql-type-json": "^0.3.2",
2544
2579
  "gray-matter": "^4.0.2",
2545
2580
  "isomorphic-git": "^1.21.0",
2581
+ "js-sha1": "^0.6.0",
2546
2582
  "js-yaml": "^3.14.1",
2547
2583
  "jsonpath-plus": "^6.0.1",
2548
2584
  leveldown: "^6.1.0",
@@ -2812,7 +2848,7 @@ import {
2812
2848
  } from "graphql";
2813
2849
 
2814
2850
  // src/resolver/index.ts
2815
- import path from "path";
2851
+ import path2 from "path";
2816
2852
  import isValid from "date-fns/isValid";
2817
2853
 
2818
2854
  // src/mdx/index.ts
@@ -3043,9 +3079,9 @@ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, s
3043
3079
  return value;
3044
3080
  }
3045
3081
  };
3046
- var cleanUpSlashes = (path4) => {
3047
- if (path4) {
3048
- return `/${path4.replace(/^\/+|\/+$/gm, "")}`;
3082
+ var cleanUpSlashes = (path5) => {
3083
+ if (path5) {
3084
+ return `/${path5.replace(/^\/+|\/+$/gm, "")}`;
3049
3085
  }
3050
3086
  return "";
3051
3087
  };
@@ -3061,6 +3097,7 @@ import { GraphQLError as GraphQLError2 } from "graphql";
3061
3097
 
3062
3098
  // src/database/datalayer.ts
3063
3099
  import { JSONPath } from "jsonpath-plus";
3100
+ import sha from "js-sha1";
3064
3101
 
3065
3102
  // src/database/level.ts
3066
3103
  var INDEX_KEY_FIELD_SEPARATOR = "";
@@ -3108,6 +3145,7 @@ var LevelProxy = class {
3108
3145
  };
3109
3146
 
3110
3147
  // src/database/datalayer.ts
3148
+ import path from "path";
3111
3149
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
3112
3150
  var DEFAULT_NUMERIC_LPAD = 4;
3113
3151
  var applyPadding = (input, pad) => {
@@ -3474,6 +3512,106 @@ var makeFilterSuffixes = (filterChain, index) => {
3474
3512
  return {};
3475
3513
  }
3476
3514
  };
3515
+ var FOLDER_ROOT = "~";
3516
+ var stripCollectionFromPath = (collectionPath, path5) => {
3517
+ const collectionPathParts = collectionPath.split("/");
3518
+ const pathParts = path5.split("/");
3519
+ const strippedPathParts = pathParts.slice(collectionPathParts.length);
3520
+ return strippedPathParts.join("/");
3521
+ };
3522
+ var FolderTreeBuilder = class {
3523
+ constructor() {
3524
+ this._tree = {
3525
+ [FOLDER_ROOT]: /* @__PURE__ */ new Set()
3526
+ };
3527
+ }
3528
+ get tree() {
3529
+ return this._tree;
3530
+ }
3531
+ update(documentPath, collectionPath) {
3532
+ let folderPath = path.dirname(documentPath);
3533
+ if (collectionPath) {
3534
+ folderPath = stripCollectionFromPath(collectionPath, folderPath);
3535
+ }
3536
+ const parent = [FOLDER_ROOT];
3537
+ folderPath.split("/").filter((part) => part.length).forEach((part) => {
3538
+ const current2 = parent.join("/");
3539
+ if (!this._tree[current2]) {
3540
+ this._tree[current2] = /* @__PURE__ */ new Set();
3541
+ }
3542
+ this._tree[current2].add(path.join(current2, part));
3543
+ parent.push(part);
3544
+ });
3545
+ const current = parent.join("/");
3546
+ if (!this._tree[current]) {
3547
+ this._tree[current] = /* @__PURE__ */ new Set();
3548
+ }
3549
+ return current === FOLDER_ROOT ? FOLDER_ROOT : sha.hex(current);
3550
+ }
3551
+ };
3552
+ var makeFolderOpsForCollection = (folderTree, collection, indexDefinitions, opType, level, escapeStr = stringEscaper) => {
3553
+ const result = [];
3554
+ const data = {};
3555
+ const indexedValues = {};
3556
+ for (const [sort, indexDefinition] of Object.entries(indexDefinitions)) {
3557
+ for (const field of indexDefinition.fields) {
3558
+ data[field.name] = "";
3559
+ }
3560
+ indexedValues[sort] = makeKeyForField(indexDefinition, data, escapeStr);
3561
+ }
3562
+ const baseCharacter = "a".charCodeAt(0);
3563
+ for (const [folderName, folder] of Object.entries(folderTree)) {
3564
+ const parentFolderKey = folderName === FOLDER_ROOT ? FOLDER_ROOT : sha.hex(folderName);
3565
+ const folderCollectionSublevel = level.sublevel(
3566
+ `${collection.name}_${parentFolderKey}`,
3567
+ SUBLEVEL_OPTIONS
3568
+ );
3569
+ let folderSortingIdx = 0;
3570
+ for (const path5 of Array.from(folder).sort()) {
3571
+ for (const [sort] of Object.entries(indexDefinitions)) {
3572
+ const indexSublevel = folderCollectionSublevel.sublevel(
3573
+ sort,
3574
+ SUBLEVEL_OPTIONS
3575
+ );
3576
+ const subFolderKey = sha.hex(path5);
3577
+ if (sort === DEFAULT_COLLECTION_SORT_KEY) {
3578
+ result.push({
3579
+ type: opType,
3580
+ key: `${collection.path}/${subFolderKey}.${collection.format}`,
3581
+ sublevel: indexSublevel,
3582
+ value: {}
3583
+ });
3584
+ } else {
3585
+ const indexValue = `${String.fromCharCode(
3586
+ baseCharacter + folderSortingIdx
3587
+ )}${indexedValues[sort].substring(1)}`;
3588
+ result.push({
3589
+ type: opType,
3590
+ key: `${indexValue}${INDEX_KEY_FIELD_SEPARATOR}${collection.path}/${subFolderKey}.${collection.format}`,
3591
+ sublevel: indexSublevel,
3592
+ value: {}
3593
+ });
3594
+ }
3595
+ }
3596
+ folderSortingIdx++;
3597
+ }
3598
+ if (folderName !== FOLDER_ROOT) {
3599
+ result.push({
3600
+ type: "put",
3601
+ key: `${collection.path}/${parentFolderKey}.${collection.format}`,
3602
+ value: {
3603
+ __folderBasename: path.basename(folderName),
3604
+ __folderPath: folderName
3605
+ },
3606
+ sublevel: level.sublevel(
3607
+ CONTENT_ROOT_PREFIX,
3608
+ SUBLEVEL_OPTIONS
3609
+ )
3610
+ });
3611
+ }
3612
+ }
3613
+ return result;
3614
+ };
3477
3615
  var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opType, level, escapeStr = stringEscaper) => {
3478
3616
  const result = [];
3479
3617
  if (collection) {
@@ -3538,11 +3676,37 @@ var Resolver = class {
3538
3676
  ...extraFields
3539
3677
  };
3540
3678
  };
3679
+ this.getRaw = async (fullPath) => {
3680
+ if (typeof fullPath !== "string") {
3681
+ throw new Error(`fullPath must be of type string for getDocument request`);
3682
+ }
3683
+ return this.database.get(fullPath);
3684
+ };
3685
+ this.getDocumentOrDirectory = async (fullPath) => {
3686
+ if (typeof fullPath !== "string") {
3687
+ throw new Error(
3688
+ `fullPath must be of type string for getDocumentOrDirectory request`
3689
+ );
3690
+ }
3691
+ const rawData = await this.getRaw(fullPath);
3692
+ if (rawData["__folderBasename"]) {
3693
+ return {
3694
+ __typename: "Folder",
3695
+ name: rawData["__folderBasename"],
3696
+ path: rawData["__folderPath"]
3697
+ };
3698
+ } else {
3699
+ return this.transformDocumentIntoPayload(fullPath, rawData);
3700
+ }
3701
+ };
3541
3702
  this.getDocument = async (fullPath) => {
3542
3703
  if (typeof fullPath !== "string") {
3543
3704
  throw new Error(`fullPath must be of type string for getDocument request`);
3544
3705
  }
3545
- const rawData = await this.database.get(fullPath);
3706
+ const rawData = await this.getRaw(fullPath);
3707
+ return this.transformDocumentIntoPayload(fullPath, rawData);
3708
+ };
3709
+ this.transformDocumentIntoPayload = async (fullPath, rawData) => {
3546
3710
  const collection = this.tinaSchema.getCollection(rawData._collection);
3547
3711
  try {
3548
3712
  const template = await this.tinaSchema.getTemplateForData({
@@ -3553,7 +3717,7 @@ var Resolver = class {
3553
3717
  base: basename,
3554
3718
  ext: extension,
3555
3719
  name: filename
3556
- } = path.parse(fullPath);
3720
+ } = path2.parse(fullPath);
3557
3721
  const relativePath = fullPath.replace(/\\/g, "/").replace(collection.path, "").replace(/^\/|\/$/g, "");
3558
3722
  const breadcrumbs = relativePath.replace(extension, "").split("/");
3559
3723
  const data = {
@@ -3585,7 +3749,7 @@ var Resolver = class {
3585
3749
  id: fullPath,
3586
3750
  ...data,
3587
3751
  _sys: {
3588
- title,
3752
+ title: title || "",
3589
3753
  basename,
3590
3754
  filename,
3591
3755
  extension,
@@ -3796,7 +3960,7 @@ var Resolver = class {
3796
3960
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
3797
3961
  );
3798
3962
  const collection = await this.tinaSchema.getCollection(collectionLookup);
3799
- const realPath = path.join(collection?.path, args.relativePath);
3963
+ const realPath = path2.join(collection?.path, args.relativePath);
3800
3964
  const alreadyExists = await this.database.documentExists(realPath);
3801
3965
  if (isMutation) {
3802
3966
  if (isCreation) {
@@ -3837,7 +4001,7 @@ var Resolver = class {
3837
4001
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
3838
4002
  );
3839
4003
  const doc = await this.getDocument(realPath);
3840
- const newRealPath = path.join(
4004
+ const newRealPath = path2.join(
3841
4005
  collection?.path,
3842
4006
  args.params.relativePath
3843
4007
  );
@@ -3892,7 +4056,7 @@ var Resolver = class {
3892
4056
  first: -1
3893
4057
  },
3894
4058
  collection: referencedCollection,
3895
- hydrator: (path4) => path4
4059
+ hydrator: (path5) => path5
3896
4060
  }
3897
4061
  );
3898
4062
  const { edges } = resolvedCollectionConnection;
@@ -3940,11 +4104,12 @@ var Resolver = class {
3940
4104
  first: args.first,
3941
4105
  last: args.last,
3942
4106
  before: args.before,
3943
- after: args.after
4107
+ after: args.after,
4108
+ folder: args.folder
3944
4109
  };
3945
4110
  const result = await this.database.query(
3946
4111
  queryOptions,
3947
- hydrator ? hydrator : this.getDocument
4112
+ hydrator ? hydrator : this.getDocumentOrDirectory
3948
4113
  );
3949
4114
  const edges = result.edges;
3950
4115
  const pageInfo = result.pageInfo;
@@ -4421,7 +4586,7 @@ var resolve = async ({
4421
4586
  };
4422
4587
 
4423
4588
  // src/database/index.ts
4424
- import path2 from "path";
4589
+ import path3 from "path";
4425
4590
  import { GraphQLError as GraphQLError4 } from "graphql";
4426
4591
  import micromatch from "micromatch";
4427
4592
 
@@ -4627,6 +4792,7 @@ var _applyNameOverrides = (fields, obj) => {
4627
4792
  };
4628
4793
 
4629
4794
  // src/database/index.ts
4795
+ import sha2 from "js-sha1";
4630
4796
  var createDatabase = (config) => {
4631
4797
  return new Database({
4632
4798
  ...config,
@@ -4645,14 +4811,14 @@ var Database = class {
4645
4811
  const tinaSchema = await this.getSchema(this.level);
4646
4812
  return tinaSchema.getCollectionByFullPath(filepath);
4647
4813
  };
4648
- this.getGeneratedFolder = () => path2.join(this.tinaDirectory, "__generated__");
4814
+ this.getGeneratedFolder = () => path3.join(this.tinaDirectory, "__generated__");
4649
4815
  this.get = async (filepath) => {
4650
4816
  await this.initLevel();
4651
4817
  if (SYSTEM_FILES.includes(filepath)) {
4652
4818
  throw new Error(`Unexpected get for config file ${filepath}`);
4653
4819
  } else {
4654
4820
  const tinaSchema = await this.getSchema(this.level);
4655
- const extension = path2.extname(filepath);
4821
+ const extension = path3.extname(filepath);
4656
4822
  const contentObject = await this.level.sublevel(
4657
4823
  CONTENT_ROOT_PREFIX,
4658
4824
  SUBLEVEL_OPTIONS
@@ -4707,26 +4873,48 @@ var Database = class {
4707
4873
  await this.bridge.put(normalizedPath, stringifiedFile);
4708
4874
  }
4709
4875
  await this.onPut(normalizedPath, stringifiedFile);
4710
- const putOps = makeIndexOpsForDocument(
4711
- normalizedPath,
4712
- collection?.name,
4713
- collectionIndexDefinitions,
4714
- dataFields,
4715
- "put",
4716
- this.level
4717
- );
4876
+ const folderTreeBuilder = new FolderTreeBuilder();
4877
+ const folderKey = folderTreeBuilder.update(filepath, collection.path || "");
4878
+ const putOps = [
4879
+ ...makeIndexOpsForDocument(
4880
+ normalizedPath,
4881
+ collection?.name,
4882
+ collectionIndexDefinitions,
4883
+ dataFields,
4884
+ "put",
4885
+ this.level
4886
+ ),
4887
+ ...makeIndexOpsForDocument(
4888
+ normalizedPath,
4889
+ `${collection?.name}_${folderKey}`,
4890
+ collectionIndexDefinitions,
4891
+ dataFields,
4892
+ "put",
4893
+ this.level
4894
+ )
4895
+ ];
4718
4896
  const existingItem = await this.level.sublevel(
4719
4897
  CONTENT_ROOT_PREFIX,
4720
4898
  SUBLEVEL_OPTIONS
4721
4899
  ).get(normalizedPath);
4722
- const delOps = existingItem ? makeIndexOpsForDocument(
4723
- normalizedPath,
4724
- collection?.name,
4725
- collectionIndexDefinitions,
4726
- existingItem,
4727
- "del",
4728
- this.level
4729
- ) : [];
4900
+ const delOps = existingItem ? [
4901
+ ...makeIndexOpsForDocument(
4902
+ normalizedPath,
4903
+ collection?.name,
4904
+ collectionIndexDefinitions,
4905
+ existingItem,
4906
+ "del",
4907
+ this.level
4908
+ ),
4909
+ ...makeIndexOpsForDocument(
4910
+ normalizedPath,
4911
+ `${collection?.name}_${folderKey}`,
4912
+ collectionIndexDefinitions,
4913
+ existingItem,
4914
+ "del",
4915
+ this.level
4916
+ )
4917
+ ] : [];
4730
4918
  const ops = [
4731
4919
  ...delOps,
4732
4920
  ...putOps,
@@ -4776,26 +4964,51 @@ var Database = class {
4776
4964
  await this.bridge.put(normalizedPath, stringifiedFile);
4777
4965
  }
4778
4966
  await this.onPut(normalizedPath, stringifiedFile);
4779
- const putOps = makeIndexOpsForDocument(
4780
- normalizedPath,
4781
- collectionName,
4782
- collectionIndexDefinitions,
4783
- dataFields,
4784
- "put",
4785
- this.level
4967
+ const folderTreeBuilder = new FolderTreeBuilder();
4968
+ const folderKey = folderTreeBuilder.update(
4969
+ filepath,
4970
+ collection.path || ""
4786
4971
  );
4972
+ const putOps = [
4973
+ ...makeIndexOpsForDocument(
4974
+ normalizedPath,
4975
+ collectionName,
4976
+ collectionIndexDefinitions,
4977
+ dataFields,
4978
+ "put",
4979
+ this.level
4980
+ ),
4981
+ ...makeIndexOpsForDocument(
4982
+ normalizedPath,
4983
+ `${collection?.name}_${folderKey}`,
4984
+ collectionIndexDefinitions,
4985
+ dataFields,
4986
+ "put",
4987
+ this.level
4988
+ )
4989
+ ];
4787
4990
  const existingItem = await this.level.sublevel(
4788
4991
  CONTENT_ROOT_PREFIX,
4789
4992
  SUBLEVEL_OPTIONS
4790
4993
  ).get(normalizedPath);
4791
- const delOps = existingItem ? makeIndexOpsForDocument(
4792
- normalizedPath,
4793
- collectionName,
4794
- collectionIndexDefinitions,
4795
- existingItem,
4796
- "del",
4797
- this.level
4798
- ) : [];
4994
+ const delOps = existingItem ? [
4995
+ ...makeIndexOpsForDocument(
4996
+ normalizedPath,
4997
+ collectionName,
4998
+ collectionIndexDefinitions,
4999
+ existingItem,
5000
+ "del",
5001
+ this.level
5002
+ ),
5003
+ ...makeIndexOpsForDocument(
5004
+ normalizedPath,
5005
+ `${collection?.name}_${folderKey}`,
5006
+ collectionIndexDefinitions,
5007
+ existingItem,
5008
+ "del",
5009
+ this.level
5010
+ )
5011
+ ] : [];
4799
5012
  const ops = [
4800
5013
  ...delOps,
4801
5014
  ...putOps,
@@ -4853,7 +5066,7 @@ var Database = class {
4853
5066
  );
4854
5067
  const writeTemplateKey = templateDetails.info.type === "union";
4855
5068
  const aliasedData = applyNameOverrides(templateDetails.template, payload);
4856
- const extension = path2.extname(filepath);
5069
+ const extension = path3.extname(filepath);
4857
5070
  const stringifiedFile = stringifyFile(
4858
5071
  aliasedData,
4859
5072
  extension,
@@ -4879,7 +5092,7 @@ var Database = class {
4879
5092
  this.getLookup = async (returnType) => {
4880
5093
  await this.initLevel();
4881
5094
  const lookupPath = normalizePath(
4882
- path2.join(this.getGeneratedFolder(), `_lookup.json`)
5095
+ path3.join(this.getGeneratedFolder(), `_lookup.json`)
4883
5096
  );
4884
5097
  if (!this._lookup) {
4885
5098
  const _lookup = await this.level.sublevel(
@@ -4893,7 +5106,7 @@ var Database = class {
4893
5106
  this.getGraphQLSchema = async () => {
4894
5107
  await this.initLevel();
4895
5108
  const graphqlPath = normalizePath(
4896
- path2.join(this.getGeneratedFolder(), `_graphql.json`)
5109
+ path3.join(this.getGeneratedFolder(), `_graphql.json`)
4897
5110
  );
4898
5111
  return await this.level.sublevel(
4899
5112
  CONTENT_ROOT_PREFIX,
@@ -4905,7 +5118,7 @@ var Database = class {
4905
5118
  throw new Error(`No bridge configured`);
4906
5119
  }
4907
5120
  const graphqlPath = normalizePath(
4908
- path2.join(this.getGeneratedFolder(), `_graphql.json`)
5121
+ path3.join(this.getGeneratedFolder(), `_graphql.json`)
4909
5122
  );
4910
5123
  const _graphql = await this.bridge.get(graphqlPath);
4911
5124
  return JSON.parse(_graphql);
@@ -4913,7 +5126,7 @@ var Database = class {
4913
5126
  this.getTinaSchema = async (level) => {
4914
5127
  await this.initLevel();
4915
5128
  const schemaPath = normalizePath(
4916
- path2.join(this.getGeneratedFolder(), `_schema.json`)
5129
+ path3.join(this.getGeneratedFolder(), `_schema.json`)
4917
5130
  );
4918
5131
  return await (level || this.level).sublevel(
4919
5132
  CONTENT_ROOT_PREFIX,
@@ -4929,7 +5142,7 @@ var Database = class {
4929
5142
  if (!schema) {
4930
5143
  throw new Error(
4931
5144
  `Unable to get schema from level db: ${normalizePath(
4932
- path2.join(this.getGeneratedFolder(), `_schema.json`)
5145
+ path3.join(this.getGeneratedFolder(), `_schema.json`)
4933
5146
  )}`
4934
5147
  );
4935
5148
  }
@@ -5003,7 +5216,8 @@ var Database = class {
5003
5216
  before,
5004
5217
  sort = DEFAULT_COLLECTION_SORT_KEY,
5005
5218
  collection,
5006
- filterChain: rawFilterChain
5219
+ filterChain: rawFilterChain,
5220
+ folder
5007
5221
  } = queryOptions;
5008
5222
  let limit = 50;
5009
5223
  if (first) {
@@ -5031,12 +5245,15 @@ var Database = class {
5031
5245
  CONTENT_ROOT_PREFIX,
5032
5246
  SUBLEVEL_OPTIONS
5033
5247
  );
5034
- const sublevel = indexDefinition ? this.level.sublevel(collection, SUBLEVEL_OPTIONS).sublevel(sort, SUBLEVEL_OPTIONS) : rootLevel;
5248
+ const sublevel = indexDefinition ? this.level.sublevel(
5249
+ `${collection}${folder ? `_${folder === FOLDER_ROOT ? folder : sha2.hex(folder)}` : ""}`,
5250
+ SUBLEVEL_OPTIONS
5251
+ ).sublevel(sort, SUBLEVEL_OPTIONS) : rootLevel;
5035
5252
  if (!query.gt && !query.gte) {
5036
5253
  query.gte = filterSuffixes?.left ? filterSuffixes.left : "";
5037
5254
  }
5038
5255
  if (!query.lt && !query.lte) {
5039
- query.lte = filterSuffixes?.right ? `${filterSuffixes.right}\xFF` : "\xFF";
5256
+ query.lte = filterSuffixes?.right ? `${filterSuffixes.right}\uFFFF` : "\uFFFF";
5040
5257
  }
5041
5258
  let edges = [];
5042
5259
  let startKey = "";
@@ -5105,11 +5322,11 @@ var Database = class {
5105
5322
  }) => {
5106
5323
  if (this.bridge && this.bridge.supportsBuilding()) {
5107
5324
  await this.bridge.putConfig(
5108
- normalizePath(path2.join(this.getGeneratedFolder(), `_graphql.json`)),
5325
+ normalizePath(path3.join(this.getGeneratedFolder(), `_graphql.json`)),
5109
5326
  JSON.stringify(graphQLSchema)
5110
5327
  );
5111
5328
  await this.bridge.putConfig(
5112
- normalizePath(path2.join(this.getGeneratedFolder(), `_schema.json`)),
5329
+ normalizePath(path3.join(this.getGeneratedFolder(), `_schema.json`)),
5113
5330
  JSON.stringify(tinaSchema.schema)
5114
5331
  );
5115
5332
  }
@@ -5129,7 +5346,7 @@ var Database = class {
5129
5346
  const lookup = lookupFromLockFile || JSON.parse(
5130
5347
  await this.bridge.get(
5131
5348
  normalizePath(
5132
- path2.join(this.getGeneratedFolder(), "_lookup.json")
5349
+ path3.join(this.getGeneratedFolder(), "_lookup.json")
5133
5350
  )
5134
5351
  )
5135
5352
  );
@@ -5144,15 +5361,15 @@ var Database = class {
5144
5361
  }
5145
5362
  const contentRootLevel = nextLevel.sublevel(CONTENT_ROOT_PREFIX, SUBLEVEL_OPTIONS);
5146
5363
  await contentRootLevel.put(
5147
- normalizePath(path2.join(this.getGeneratedFolder(), "_graphql.json")),
5364
+ normalizePath(path3.join(this.getGeneratedFolder(), "_graphql.json")),
5148
5365
  graphQLSchema
5149
5366
  );
5150
5367
  await contentRootLevel.put(
5151
- normalizePath(path2.join(this.getGeneratedFolder(), "_schema.json")),
5368
+ normalizePath(path3.join(this.getGeneratedFolder(), "_schema.json")),
5152
5369
  tinaSchema.schema
5153
5370
  );
5154
5371
  await contentRootLevel.put(
5155
- normalizePath(path2.join(this.getGeneratedFolder(), "_lookup.json")),
5372
+ normalizePath(path3.join(this.getGeneratedFolder(), "_lookup.json")),
5156
5373
  lookup
5157
5374
  );
5158
5375
  const result = await this._indexAllContent(
@@ -5193,7 +5410,9 @@ var Database = class {
5193
5410
  collections[collection]
5194
5411
  );
5195
5412
  }
5196
- await _deleteIndexContent(this, nonCollectionPaths, enqueueOps, null);
5413
+ if (nonCollectionPaths.length) {
5414
+ await _deleteIndexContent(this, nonCollectionPaths, enqueueOps, null);
5415
+ }
5197
5416
  });
5198
5417
  while (operations.length) {
5199
5418
  await this.level.batch(operations.splice(0, 25));
@@ -5244,6 +5463,11 @@ var Database = class {
5244
5463
  );
5245
5464
  const item = await rootSublevel.get(itemKey);
5246
5465
  if (item) {
5466
+ const folderTreeBuilder = new FolderTreeBuilder();
5467
+ const folderKey = folderTreeBuilder.update(
5468
+ filepath,
5469
+ collection.path || ""
5470
+ );
5247
5471
  await this.level.batch([
5248
5472
  ...makeIndexOpsForDocument(
5249
5473
  filepath,
@@ -5253,6 +5477,14 @@ var Database = class {
5253
5477
  "del",
5254
5478
  this.level
5255
5479
  ),
5480
+ ...makeIndexOpsForDocument(
5481
+ filepath,
5482
+ `${collection.name}_${folderKey}`,
5483
+ collectionIndexDefinitions,
5484
+ item,
5485
+ "del",
5486
+ this.level
5487
+ ),
5256
5488
  {
5257
5489
  type: "del",
5258
5490
  key: itemKey,
@@ -5284,17 +5516,17 @@ var Database = class {
5284
5516
  const documentPaths = await this.bridge.glob(normalPath, format);
5285
5517
  const matches = this.tinaSchema.getMatches({ collection });
5286
5518
  const filteredPaths = matches.length > 0 ? micromatch(documentPaths, matches) : documentPaths;
5287
- filteredPaths.forEach((path4) => {
5288
- if (filesSeen.has(path4)) {
5289
- filesSeen.get(path4).push(collection.name);
5290
- duplicateFiles.add(path4);
5519
+ filteredPaths.forEach((path5) => {
5520
+ if (filesSeen.has(path5)) {
5521
+ filesSeen.get(path5).push(collection.name);
5522
+ duplicateFiles.add(path5);
5291
5523
  } else {
5292
- filesSeen.set(path4, [collection.name]);
5524
+ filesSeen.set(path5, [collection.name]);
5293
5525
  }
5294
5526
  });
5295
- duplicateFiles.forEach((path4) => {
5527
+ duplicateFiles.forEach((path5) => {
5296
5528
  warnings.push(
5297
- `"${path4}" Found in multiple collections: ${filesSeen.get(path4).map((collection2) => `"${collection2}"`).join(
5529
+ `"${path5}" Found in multiple collections: ${filesSeen.get(path5).map((collection2) => `"${collection2}"`).join(
5298
5530
  ", "
5299
5531
  )}. This can cause unexpected behavior. We recommend updating the \`match\` property of those collections so that each file is in only one collection.
5300
5532
  This will be an error in the future. See https://tina.io/docs/errors/file-in-mutpliple-collections/
@@ -5312,7 +5544,7 @@ This will be an error in the future. See https://tina.io/docs/errors/file-in-mut
5312
5544
  if (!this.bridge) {
5313
5545
  throw new Error("No bridge configured");
5314
5546
  }
5315
- const lookupPath = path2.join(this.getGeneratedFolder(), `_lookup.json`);
5547
+ const lookupPath = path3.join(this.getGeneratedFolder(), `_lookup.json`);
5316
5548
  let lookupMap;
5317
5549
  try {
5318
5550
  lookupMap = JSON.parse(await this.bridge.get(normalizePath(lookupPath)));
@@ -5430,24 +5662,27 @@ function hasOwnProperty(obj, prop) {
5430
5662
  }
5431
5663
  var _indexContent = async (database, level, documentPaths, enqueueOps, collection) => {
5432
5664
  let collectionIndexDefinitions;
5665
+ let collectionPath;
5433
5666
  if (collection) {
5434
5667
  const indexDefinitions = await database.getIndexDefinitions(level);
5435
5668
  collectionIndexDefinitions = indexDefinitions?.[collection.name];
5436
5669
  if (!collectionIndexDefinitions) {
5437
5670
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
5438
5671
  }
5672
+ collectionPath = collection.path;
5439
5673
  }
5440
5674
  const tinaSchema = await database.getSchema();
5441
5675
  let templateInfo = null;
5442
5676
  if (collection) {
5443
5677
  templateInfo = await tinaSchema.getTemplatesForCollectable(collection);
5444
5678
  }
5679
+ const folderTreeBuilder = new FolderTreeBuilder();
5445
5680
  await sequential(documentPaths, async (filepath) => {
5446
5681
  try {
5447
5682
  const dataString = await database.bridge.get(normalizePath(filepath));
5448
5683
  const data = parseFile(
5449
5684
  dataString,
5450
- path2.extname(filepath),
5685
+ path3.extname(filepath),
5451
5686
  (yup3) => yup3.object({}),
5452
5687
  {
5453
5688
  frontmatterDelimiters: collection?.frontmatterDelimiters,
@@ -5462,6 +5697,10 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
5462
5697
  return;
5463
5698
  }
5464
5699
  const normalizedPath = normalizePath(filepath);
5700
+ const folderKey = folderTreeBuilder.update(
5701
+ normalizedPath,
5702
+ collectionPath || ""
5703
+ );
5465
5704
  const aliasedData = templateInfo ? replaceNameOverrides(template, data) : data;
5466
5705
  await enqueueOps([
5467
5706
  ...makeIndexOpsForDocument(
@@ -5472,6 +5711,14 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
5472
5711
  "put",
5473
5712
  level
5474
5713
  ),
5714
+ ...makeIndexOpsForDocument(
5715
+ normalizedPath,
5716
+ `${collection?.name}_${folderKey}`,
5717
+ collectionIndexDefinitions,
5718
+ aliasedData,
5719
+ "put",
5720
+ level
5721
+ ),
5475
5722
  {
5476
5723
  type: "put",
5477
5724
  key: normalizedPath,
@@ -5491,8 +5738,22 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
5491
5738
  });
5492
5739
  }
5493
5740
  });
5741
+ if (collection) {
5742
+ await enqueueOps(
5743
+ makeFolderOpsForCollection(
5744
+ folderTreeBuilder.tree,
5745
+ collection,
5746
+ collectionIndexDefinitions,
5747
+ "put",
5748
+ level
5749
+ )
5750
+ );
5751
+ }
5494
5752
  };
5495
- var _deleteIndexContent = async (database, documentPaths, enequeueOps, collection) => {
5753
+ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection) => {
5754
+ if (!documentPaths.length) {
5755
+ return;
5756
+ }
5496
5757
  let collectionIndexDefinitions;
5497
5758
  if (collection) {
5498
5759
  const indexDefinitions = await database.getIndexDefinitions(database.level);
@@ -5501,20 +5762,42 @@ var _deleteIndexContent = async (database, documentPaths, enequeueOps, collectio
5501
5762
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
5502
5763
  }
5503
5764
  }
5765
+ const tinaSchema = await database.getSchema();
5766
+ let templateInfo = null;
5767
+ if (collection) {
5768
+ templateInfo = await tinaSchema.getTemplatesForCollectable(collection);
5769
+ }
5504
5770
  const rootLevel = database.level.sublevel(
5505
5771
  CONTENT_ROOT_PREFIX,
5506
5772
  SUBLEVEL_OPTIONS
5507
5773
  );
5774
+ const folderTreeBuilder = new FolderTreeBuilder();
5508
5775
  await sequential(documentPaths, async (filepath) => {
5509
5776
  const itemKey = normalizePath(filepath);
5510
5777
  const item = await rootLevel.get(itemKey);
5511
5778
  if (item) {
5512
- await enequeueOps([
5779
+ const folderKey = folderTreeBuilder.update(
5780
+ itemKey,
5781
+ collection?.path || ""
5782
+ );
5783
+ const aliasedData = templateInfo ? replaceNameOverrides(
5784
+ getTemplateForFile(templateInfo, item),
5785
+ item
5786
+ ) : item;
5787
+ await enqueueOps([
5513
5788
  ...makeIndexOpsForDocument(
5514
5789
  itemKey,
5515
5790
  collection.name,
5516
5791
  collectionIndexDefinitions,
5517
- item,
5792
+ aliasedData,
5793
+ "del",
5794
+ database.level
5795
+ ),
5796
+ ...makeIndexOpsForDocument(
5797
+ itemKey,
5798
+ `${collection?.name}_${folderKey}`,
5799
+ collectionIndexDefinitions,
5800
+ aliasedData,
5518
5801
  "del",
5519
5802
  database.level
5520
5803
  ),
@@ -5522,6 +5805,17 @@ var _deleteIndexContent = async (database, documentPaths, enequeueOps, collectio
5522
5805
  ]);
5523
5806
  }
5524
5807
  });
5808
+ if (collectionIndexDefinitions) {
5809
+ await enqueueOps(
5810
+ makeFolderOpsForCollection(
5811
+ folderTreeBuilder.tree,
5812
+ collection,
5813
+ collectionIndexDefinitions,
5814
+ "del",
5815
+ database.level
5816
+ )
5817
+ );
5818
+ }
5525
5819
  };
5526
5820
  var getTemplateForFile = (templateInfo, data) => {
5527
5821
  if (templateInfo.type === "object") {
@@ -5569,7 +5863,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
5569
5863
  // src/database/bridge/filesystem.ts
5570
5864
  import fs from "fs-extra";
5571
5865
  import fg from "fast-glob";
5572
- import path3 from "path";
5866
+ import path4 from "path";
5573
5867
  import normalize from "normalize-path";
5574
5868
  var FilesystemBridge = class {
5575
5869
  constructor(rootPath, outputPath) {
@@ -5577,9 +5871,9 @@ var FilesystemBridge = class {
5577
5871
  this.outputPath = outputPath || rootPath;
5578
5872
  }
5579
5873
  async glob(pattern, extension) {
5580
- const basePath = path3.join(this.outputPath, ...pattern.split("/"));
5874
+ const basePath = path4.join(this.outputPath, ...pattern.split("/"));
5581
5875
  const items = await fg(
5582
- path3.join(basePath, "**", `/*${extension}`).replace(/\\/g, "/"),
5876
+ path4.join(basePath, "**", `/*${extension}`).replace(/\\/g, "/"),
5583
5877
  {
5584
5878
  dot: true
5585
5879
  }
@@ -5593,10 +5887,10 @@ var FilesystemBridge = class {
5593
5887
  return true;
5594
5888
  }
5595
5889
  async delete(filepath) {
5596
- await fs.remove(path3.join(this.outputPath, filepath));
5890
+ await fs.remove(path4.join(this.outputPath, filepath));
5597
5891
  }
5598
5892
  async get(filepath) {
5599
- return fs.readFileSync(path3.join(this.outputPath, filepath)).toString();
5893
+ return fs.readFileSync(path4.join(this.outputPath, filepath)).toString();
5600
5894
  }
5601
5895
  async putConfig(filepath, data) {
5602
5896
  if (this.rootPath !== this.outputPath) {
@@ -5608,7 +5902,7 @@ var FilesystemBridge = class {
5608
5902
  }
5609
5903
  async put(filepath, data, basePathOverride) {
5610
5904
  const basePath = basePathOverride || this.outputPath;
5611
- await fs.outputFileSync(path3.join(basePath, filepath), data);
5905
+ await fs.outputFileSync(path4.join(basePath, filepath), data);
5612
5906
  }
5613
5907
  };
5614
5908
  var AuditFileSystemBridge = class extends FilesystemBridge {
@@ -5689,7 +5983,7 @@ var IsomorphicBridge = class {
5689
5983
  async listEntries({
5690
5984
  pattern,
5691
5985
  entry,
5692
- path: path4,
5986
+ path: path5,
5693
5987
  results
5694
5988
  }) {
5695
5989
  const treeResult = await git.readTree({
@@ -5699,7 +5993,7 @@ var IsomorphicBridge = class {
5699
5993
  });
5700
5994
  const children = [];
5701
5995
  for (const childEntry of treeResult.tree) {
5702
- const childPath = path4 ? `${path4}/${childEntry.path}` : childEntry.path;
5996
+ const childPath = path5 ? `${path5}/${childEntry.path}` : childEntry.path;
5703
5997
  if (childEntry.type === "tree") {
5704
5998
  children.push(childEntry);
5705
5999
  } else {
@@ -5709,7 +6003,7 @@ var IsomorphicBridge = class {
5709
6003
  }
5710
6004
  }
5711
6005
  for (const childEntry of children) {
5712
- const childPath = path4 ? `${path4}/${childEntry.path}` : childEntry.path;
6006
+ const childPath = path5 ? `${path5}/${childEntry.path}` : childEntry.path;
5713
6007
  await this.listEntries({
5714
6008
  pattern,
5715
6009
  entry: childEntry,
@@ -5718,17 +6012,17 @@ var IsomorphicBridge = class {
5718
6012
  });
5719
6013
  }
5720
6014
  }
5721
- async resolvePathEntries(path4, ref) {
5722
- let pathParts = path4.split("/");
6015
+ async resolvePathEntries(path5, ref) {
6016
+ let pathParts = path5.split("/");
5723
6017
  const result = await git.walk({
5724
6018
  ...this.isomorphicConfig,
5725
6019
  map: async (filepath, [head]) => {
5726
6020
  if (head._fullpath === ".") {
5727
6021
  return head;
5728
6022
  }
5729
- if (path4.startsWith(filepath)) {
5730
- if (dirname(path4) === dirname(filepath)) {
5731
- if (path4 === filepath) {
6023
+ if (path5.startsWith(filepath)) {
6024
+ if (dirname(path5) === dirname(filepath)) {
6025
+ if (path5 === filepath) {
5732
6026
  return head;
5733
6027
  }
5734
6028
  } else {
@@ -5748,7 +6042,7 @@ var IsomorphicBridge = class {
5748
6042
  }
5749
6043
  return { pathParts, pathEntries };
5750
6044
  }
5751
- async updateTreeHierarchy(existingOid, updatedOid, path4, type, pathEntries, pathParts) {
6045
+ async updateTreeHierarchy(existingOid, updatedOid, path5, type, pathEntries, pathParts) {
5752
6046
  const lastIdx = pathEntries.length - 1;
5753
6047
  const parentEntry = pathEntries[lastIdx];
5754
6048
  const parentPath = pathParts[lastIdx];
@@ -5763,7 +6057,7 @@ var IsomorphicBridge = class {
5763
6057
  cache: this.cache
5764
6058
  });
5765
6059
  tree = existingOid ? treeResult.tree.map((entry) => {
5766
- if (entry.path === path4) {
6060
+ if (entry.path === path5) {
5767
6061
  entry.oid = updatedOid;
5768
6062
  }
5769
6063
  return entry;
@@ -5772,7 +6066,7 @@ var IsomorphicBridge = class {
5772
6066
  {
5773
6067
  oid: updatedOid,
5774
6068
  type,
5775
- path: path4,
6069
+ path: path5,
5776
6070
  mode
5777
6071
  }
5778
6072
  ];
@@ -5781,7 +6075,7 @@ var IsomorphicBridge = class {
5781
6075
  {
5782
6076
  oid: updatedOid,
5783
6077
  type,
5784
- path: path4,
6078
+ path: path5,
5785
6079
  mode
5786
6080
  }
5787
6081
  ];
@@ -5882,7 +6176,7 @@ var IsomorphicBridge = class {
5882
6176
  path: parentPath,
5883
6177
  results
5884
6178
  });
5885
- return results.map((path4) => this.unqualifyPath(path4)).filter((path4) => path4.endsWith(extension));
6179
+ return results.map((path5) => this.unqualifyPath(path5)).filter((path5) => path5.endsWith(extension));
5886
6180
  }
5887
6181
  supportsBuilding() {
5888
6182
  return true;