@tinacms/graphql 1.4.7 → 1.4.9

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.9",
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,107 @@ 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
+ __collection: collection.name,
3604
+ __folderBasename: path.basename(folderName),
3605
+ __folderPath: folderName
3606
+ },
3607
+ sublevel: level.sublevel(
3608
+ CONTENT_ROOT_PREFIX,
3609
+ SUBLEVEL_OPTIONS
3610
+ )
3611
+ });
3612
+ }
3613
+ }
3614
+ return result;
3615
+ };
3477
3616
  var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opType, level, escapeStr = stringEscaper) => {
3478
3617
  const result = [];
3479
3618
  if (collection) {
@@ -3538,11 +3677,37 @@ var Resolver = class {
3538
3677
  ...extraFields
3539
3678
  };
3540
3679
  };
3680
+ this.getRaw = async (fullPath) => {
3681
+ if (typeof fullPath !== "string") {
3682
+ throw new Error(`fullPath must be of type string for getDocument request`);
3683
+ }
3684
+ return this.database.get(fullPath);
3685
+ };
3686
+ this.getDocumentOrDirectory = async (fullPath) => {
3687
+ if (typeof fullPath !== "string") {
3688
+ throw new Error(
3689
+ `fullPath must be of type string for getDocumentOrDirectory request`
3690
+ );
3691
+ }
3692
+ const rawData = await this.getRaw(fullPath);
3693
+ if (rawData["__folderBasename"]) {
3694
+ return {
3695
+ __typename: "Folder",
3696
+ name: rawData["__folderBasename"],
3697
+ path: rawData["__folderPath"]
3698
+ };
3699
+ } else {
3700
+ return this.transformDocumentIntoPayload(fullPath, rawData);
3701
+ }
3702
+ };
3541
3703
  this.getDocument = async (fullPath) => {
3542
3704
  if (typeof fullPath !== "string") {
3543
3705
  throw new Error(`fullPath must be of type string for getDocument request`);
3544
3706
  }
3545
- const rawData = await this.database.get(fullPath);
3707
+ const rawData = await this.getRaw(fullPath);
3708
+ return this.transformDocumentIntoPayload(fullPath, rawData);
3709
+ };
3710
+ this.transformDocumentIntoPayload = async (fullPath, rawData) => {
3546
3711
  const collection = this.tinaSchema.getCollection(rawData._collection);
3547
3712
  try {
3548
3713
  const template = await this.tinaSchema.getTemplateForData({
@@ -3553,7 +3718,7 @@ var Resolver = class {
3553
3718
  base: basename,
3554
3719
  ext: extension,
3555
3720
  name: filename
3556
- } = path.parse(fullPath);
3721
+ } = path2.parse(fullPath);
3557
3722
  const relativePath = fullPath.replace(/\\/g, "/").replace(collection.path, "").replace(/^\/|\/$/g, "");
3558
3723
  const breadcrumbs = relativePath.replace(extension, "").split("/");
3559
3724
  const data = {
@@ -3585,7 +3750,7 @@ var Resolver = class {
3585
3750
  id: fullPath,
3586
3751
  ...data,
3587
3752
  _sys: {
3588
- title,
3753
+ title: title || "",
3589
3754
  basename,
3590
3755
  filename,
3591
3756
  extension,
@@ -3796,7 +3961,7 @@ var Resolver = class {
3796
3961
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
3797
3962
  );
3798
3963
  const collection = await this.tinaSchema.getCollection(collectionLookup);
3799
- const realPath = path.join(collection?.path, args.relativePath);
3964
+ const realPath = path2.join(collection?.path, args.relativePath);
3800
3965
  const alreadyExists = await this.database.documentExists(realPath);
3801
3966
  if (isMutation) {
3802
3967
  if (isCreation) {
@@ -3837,7 +4002,7 @@ var Resolver = class {
3837
4002
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
3838
4003
  );
3839
4004
  const doc = await this.getDocument(realPath);
3840
- const newRealPath = path.join(
4005
+ const newRealPath = path2.join(
3841
4006
  collection?.path,
3842
4007
  args.params.relativePath
3843
4008
  );
@@ -3892,7 +4057,7 @@ var Resolver = class {
3892
4057
  first: -1
3893
4058
  },
3894
4059
  collection: referencedCollection,
3895
- hydrator: (path4) => path4
4060
+ hydrator: (path5) => path5
3896
4061
  }
3897
4062
  );
3898
4063
  const { edges } = resolvedCollectionConnection;
@@ -3940,11 +4105,12 @@ var Resolver = class {
3940
4105
  first: args.first,
3941
4106
  last: args.last,
3942
4107
  before: args.before,
3943
- after: args.after
4108
+ after: args.after,
4109
+ folder: args.folder
3944
4110
  };
3945
4111
  const result = await this.database.query(
3946
4112
  queryOptions,
3947
- hydrator ? hydrator : this.getDocument
4113
+ hydrator ? hydrator : this.getDocumentOrDirectory
3948
4114
  );
3949
4115
  const edges = result.edges;
3950
4116
  const pageInfo = result.pageInfo;
@@ -4421,7 +4587,7 @@ var resolve = async ({
4421
4587
  };
4422
4588
 
4423
4589
  // src/database/index.ts
4424
- import path2 from "path";
4590
+ import path3 from "path";
4425
4591
  import { GraphQLError as GraphQLError4 } from "graphql";
4426
4592
  import micromatch from "micromatch";
4427
4593
 
@@ -4627,6 +4793,7 @@ var _applyNameOverrides = (fields, obj) => {
4627
4793
  };
4628
4794
 
4629
4795
  // src/database/index.ts
4796
+ import sha2 from "js-sha1";
4630
4797
  var createDatabase = (config) => {
4631
4798
  return new Database({
4632
4799
  ...config,
@@ -4645,14 +4812,14 @@ var Database = class {
4645
4812
  const tinaSchema = await this.getSchema(this.level);
4646
4813
  return tinaSchema.getCollectionByFullPath(filepath);
4647
4814
  };
4648
- this.getGeneratedFolder = () => path2.join(this.tinaDirectory, "__generated__");
4815
+ this.getGeneratedFolder = () => path3.join(this.tinaDirectory, "__generated__");
4649
4816
  this.get = async (filepath) => {
4650
4817
  await this.initLevel();
4651
4818
  if (SYSTEM_FILES.includes(filepath)) {
4652
4819
  throw new Error(`Unexpected get for config file ${filepath}`);
4653
4820
  } else {
4654
4821
  const tinaSchema = await this.getSchema(this.level);
4655
- const extension = path2.extname(filepath);
4822
+ const extension = path3.extname(filepath);
4656
4823
  const contentObject = await this.level.sublevel(
4657
4824
  CONTENT_ROOT_PREFIX,
4658
4825
  SUBLEVEL_OPTIONS
@@ -4661,8 +4828,16 @@ var Database = class {
4661
4828
  throw new GraphQLError4(`Unable to find record ${filepath}`);
4662
4829
  }
4663
4830
  const templateName = hasOwnProperty(contentObject, "_template") && typeof contentObject._template === "string" ? contentObject._template : void 0;
4664
- const { collection, template } = tinaSchema.getCollectionAndTemplateByFullPath(filepath, templateName);
4665
- const field = template.fields.find((field2) => {
4831
+ const { collection, template } = hasOwnProperty(
4832
+ contentObject,
4833
+ "__collection"
4834
+ ) ? {
4835
+ collection: tinaSchema.getCollection(
4836
+ contentObject["__collection"]
4837
+ ),
4838
+ template: void 0
4839
+ } : tinaSchema.getCollectionAndTemplateByFullPath(filepath, templateName);
4840
+ const field = template?.fields.find((field2) => {
4666
4841
  if (field2.type === "string" || field2.type === "rich-text") {
4667
4842
  if (field2.isBody) {
4668
4843
  return true;
@@ -4682,7 +4857,7 @@ var Database = class {
4682
4857
  ...data,
4683
4858
  _collection: collection.name,
4684
4859
  _keepTemplateKey: !!collection.templates,
4685
- _template: lastItem(template.namespace),
4860
+ _template: template?.namespace ? lastItem(template?.namespace) : void 0,
4686
4861
  _relativePath: filepath.replace(collection.path, "").replace(/^\/|\/$/g, ""),
4687
4862
  _id: filepath
4688
4863
  };
@@ -4707,26 +4882,48 @@ var Database = class {
4707
4882
  await this.bridge.put(normalizedPath, stringifiedFile);
4708
4883
  }
4709
4884
  await this.onPut(normalizedPath, stringifiedFile);
4710
- const putOps = makeIndexOpsForDocument(
4711
- normalizedPath,
4712
- collection?.name,
4713
- collectionIndexDefinitions,
4714
- dataFields,
4715
- "put",
4716
- this.level
4717
- );
4885
+ const folderTreeBuilder = new FolderTreeBuilder();
4886
+ const folderKey = folderTreeBuilder.update(filepath, collection.path || "");
4887
+ const putOps = [
4888
+ ...makeIndexOpsForDocument(
4889
+ normalizedPath,
4890
+ collection?.name,
4891
+ collectionIndexDefinitions,
4892
+ dataFields,
4893
+ "put",
4894
+ this.level
4895
+ ),
4896
+ ...makeIndexOpsForDocument(
4897
+ normalizedPath,
4898
+ `${collection?.name}_${folderKey}`,
4899
+ collectionIndexDefinitions,
4900
+ dataFields,
4901
+ "put",
4902
+ this.level
4903
+ )
4904
+ ];
4718
4905
  const existingItem = await this.level.sublevel(
4719
4906
  CONTENT_ROOT_PREFIX,
4720
4907
  SUBLEVEL_OPTIONS
4721
4908
  ).get(normalizedPath);
4722
- const delOps = existingItem ? makeIndexOpsForDocument(
4723
- normalizedPath,
4724
- collection?.name,
4725
- collectionIndexDefinitions,
4726
- existingItem,
4727
- "del",
4728
- this.level
4729
- ) : [];
4909
+ const delOps = existingItem ? [
4910
+ ...makeIndexOpsForDocument(
4911
+ normalizedPath,
4912
+ collection?.name,
4913
+ collectionIndexDefinitions,
4914
+ existingItem,
4915
+ "del",
4916
+ this.level
4917
+ ),
4918
+ ...makeIndexOpsForDocument(
4919
+ normalizedPath,
4920
+ `${collection?.name}_${folderKey}`,
4921
+ collectionIndexDefinitions,
4922
+ existingItem,
4923
+ "del",
4924
+ this.level
4925
+ )
4926
+ ] : [];
4730
4927
  const ops = [
4731
4928
  ...delOps,
4732
4929
  ...putOps,
@@ -4776,26 +4973,51 @@ var Database = class {
4776
4973
  await this.bridge.put(normalizedPath, stringifiedFile);
4777
4974
  }
4778
4975
  await this.onPut(normalizedPath, stringifiedFile);
4779
- const putOps = makeIndexOpsForDocument(
4780
- normalizedPath,
4781
- collectionName,
4782
- collectionIndexDefinitions,
4783
- dataFields,
4784
- "put",
4785
- this.level
4976
+ const folderTreeBuilder = new FolderTreeBuilder();
4977
+ const folderKey = folderTreeBuilder.update(
4978
+ filepath,
4979
+ collection.path || ""
4786
4980
  );
4981
+ const putOps = [
4982
+ ...makeIndexOpsForDocument(
4983
+ normalizedPath,
4984
+ collectionName,
4985
+ collectionIndexDefinitions,
4986
+ dataFields,
4987
+ "put",
4988
+ this.level
4989
+ ),
4990
+ ...makeIndexOpsForDocument(
4991
+ normalizedPath,
4992
+ `${collection?.name}_${folderKey}`,
4993
+ collectionIndexDefinitions,
4994
+ dataFields,
4995
+ "put",
4996
+ this.level
4997
+ )
4998
+ ];
4787
4999
  const existingItem = await this.level.sublevel(
4788
5000
  CONTENT_ROOT_PREFIX,
4789
5001
  SUBLEVEL_OPTIONS
4790
5002
  ).get(normalizedPath);
4791
- const delOps = existingItem ? makeIndexOpsForDocument(
4792
- normalizedPath,
4793
- collectionName,
4794
- collectionIndexDefinitions,
4795
- existingItem,
4796
- "del",
4797
- this.level
4798
- ) : [];
5003
+ const delOps = existingItem ? [
5004
+ ...makeIndexOpsForDocument(
5005
+ normalizedPath,
5006
+ collectionName,
5007
+ collectionIndexDefinitions,
5008
+ existingItem,
5009
+ "del",
5010
+ this.level
5011
+ ),
5012
+ ...makeIndexOpsForDocument(
5013
+ normalizedPath,
5014
+ `${collection?.name}_${folderKey}`,
5015
+ collectionIndexDefinitions,
5016
+ existingItem,
5017
+ "del",
5018
+ this.level
5019
+ )
5020
+ ] : [];
4799
5021
  const ops = [
4800
5022
  ...delOps,
4801
5023
  ...putOps,
@@ -4853,7 +5075,7 @@ var Database = class {
4853
5075
  );
4854
5076
  const writeTemplateKey = templateDetails.info.type === "union";
4855
5077
  const aliasedData = applyNameOverrides(templateDetails.template, payload);
4856
- const extension = path2.extname(filepath);
5078
+ const extension = path3.extname(filepath);
4857
5079
  const stringifiedFile = stringifyFile(
4858
5080
  aliasedData,
4859
5081
  extension,
@@ -4879,7 +5101,7 @@ var Database = class {
4879
5101
  this.getLookup = async (returnType) => {
4880
5102
  await this.initLevel();
4881
5103
  const lookupPath = normalizePath(
4882
- path2.join(this.getGeneratedFolder(), `_lookup.json`)
5104
+ path3.join(this.getGeneratedFolder(), `_lookup.json`)
4883
5105
  );
4884
5106
  if (!this._lookup) {
4885
5107
  const _lookup = await this.level.sublevel(
@@ -4893,7 +5115,7 @@ var Database = class {
4893
5115
  this.getGraphQLSchema = async () => {
4894
5116
  await this.initLevel();
4895
5117
  const graphqlPath = normalizePath(
4896
- path2.join(this.getGeneratedFolder(), `_graphql.json`)
5118
+ path3.join(this.getGeneratedFolder(), `_graphql.json`)
4897
5119
  );
4898
5120
  return await this.level.sublevel(
4899
5121
  CONTENT_ROOT_PREFIX,
@@ -4905,7 +5127,7 @@ var Database = class {
4905
5127
  throw new Error(`No bridge configured`);
4906
5128
  }
4907
5129
  const graphqlPath = normalizePath(
4908
- path2.join(this.getGeneratedFolder(), `_graphql.json`)
5130
+ path3.join(this.getGeneratedFolder(), `_graphql.json`)
4909
5131
  );
4910
5132
  const _graphql = await this.bridge.get(graphqlPath);
4911
5133
  return JSON.parse(_graphql);
@@ -4913,7 +5135,7 @@ var Database = class {
4913
5135
  this.getTinaSchema = async (level) => {
4914
5136
  await this.initLevel();
4915
5137
  const schemaPath = normalizePath(
4916
- path2.join(this.getGeneratedFolder(), `_schema.json`)
5138
+ path3.join(this.getGeneratedFolder(), `_schema.json`)
4917
5139
  );
4918
5140
  return await (level || this.level).sublevel(
4919
5141
  CONTENT_ROOT_PREFIX,
@@ -4929,7 +5151,7 @@ var Database = class {
4929
5151
  if (!schema) {
4930
5152
  throw new Error(
4931
5153
  `Unable to get schema from level db: ${normalizePath(
4932
- path2.join(this.getGeneratedFolder(), `_schema.json`)
5154
+ path3.join(this.getGeneratedFolder(), `_schema.json`)
4933
5155
  )}`
4934
5156
  );
4935
5157
  }
@@ -5003,7 +5225,8 @@ var Database = class {
5003
5225
  before,
5004
5226
  sort = DEFAULT_COLLECTION_SORT_KEY,
5005
5227
  collection,
5006
- filterChain: rawFilterChain
5228
+ filterChain: rawFilterChain,
5229
+ folder
5007
5230
  } = queryOptions;
5008
5231
  let limit = 50;
5009
5232
  if (first) {
@@ -5031,12 +5254,15 @@ var Database = class {
5031
5254
  CONTENT_ROOT_PREFIX,
5032
5255
  SUBLEVEL_OPTIONS
5033
5256
  );
5034
- const sublevel = indexDefinition ? this.level.sublevel(collection, SUBLEVEL_OPTIONS).sublevel(sort, SUBLEVEL_OPTIONS) : rootLevel;
5257
+ const sublevel = indexDefinition ? this.level.sublevel(
5258
+ `${collection}${folder ? `_${folder === FOLDER_ROOT ? folder : sha2.hex(folder)}` : ""}`,
5259
+ SUBLEVEL_OPTIONS
5260
+ ).sublevel(sort, SUBLEVEL_OPTIONS) : rootLevel;
5035
5261
  if (!query.gt && !query.gte) {
5036
5262
  query.gte = filterSuffixes?.left ? filterSuffixes.left : "";
5037
5263
  }
5038
5264
  if (!query.lt && !query.lte) {
5039
- query.lte = filterSuffixes?.right ? `${filterSuffixes.right}\xFF` : "\xFF";
5265
+ query.lte = filterSuffixes?.right ? `${filterSuffixes.right}\uFFFF` : "\uFFFF";
5040
5266
  }
5041
5267
  let edges = [];
5042
5268
  let startKey = "";
@@ -5079,6 +5305,7 @@ var Database = class {
5079
5305
  cursor: btoa(edge.cursor)
5080
5306
  };
5081
5307
  } catch (error) {
5308
+ console.log(error);
5082
5309
  if (error instanceof Error && (!edge.path.includes(".tina/__generated__/_graphql.json") || !edge.path.includes("tina/__generated__/_graphql.json"))) {
5083
5310
  throw new TinaQueryError({
5084
5311
  originalError: error,
@@ -5105,11 +5332,11 @@ var Database = class {
5105
5332
  }) => {
5106
5333
  if (this.bridge && this.bridge.supportsBuilding()) {
5107
5334
  await this.bridge.putConfig(
5108
- normalizePath(path2.join(this.getGeneratedFolder(), `_graphql.json`)),
5335
+ normalizePath(path3.join(this.getGeneratedFolder(), `_graphql.json`)),
5109
5336
  JSON.stringify(graphQLSchema)
5110
5337
  );
5111
5338
  await this.bridge.putConfig(
5112
- normalizePath(path2.join(this.getGeneratedFolder(), `_schema.json`)),
5339
+ normalizePath(path3.join(this.getGeneratedFolder(), `_schema.json`)),
5113
5340
  JSON.stringify(tinaSchema.schema)
5114
5341
  );
5115
5342
  }
@@ -5129,7 +5356,7 @@ var Database = class {
5129
5356
  const lookup = lookupFromLockFile || JSON.parse(
5130
5357
  await this.bridge.get(
5131
5358
  normalizePath(
5132
- path2.join(this.getGeneratedFolder(), "_lookup.json")
5359
+ path3.join(this.getGeneratedFolder(), "_lookup.json")
5133
5360
  )
5134
5361
  )
5135
5362
  );
@@ -5144,15 +5371,15 @@ var Database = class {
5144
5371
  }
5145
5372
  const contentRootLevel = nextLevel.sublevel(CONTENT_ROOT_PREFIX, SUBLEVEL_OPTIONS);
5146
5373
  await contentRootLevel.put(
5147
- normalizePath(path2.join(this.getGeneratedFolder(), "_graphql.json")),
5374
+ normalizePath(path3.join(this.getGeneratedFolder(), "_graphql.json")),
5148
5375
  graphQLSchema
5149
5376
  );
5150
5377
  await contentRootLevel.put(
5151
- normalizePath(path2.join(this.getGeneratedFolder(), "_schema.json")),
5378
+ normalizePath(path3.join(this.getGeneratedFolder(), "_schema.json")),
5152
5379
  tinaSchema.schema
5153
5380
  );
5154
5381
  await contentRootLevel.put(
5155
- normalizePath(path2.join(this.getGeneratedFolder(), "_lookup.json")),
5382
+ normalizePath(path3.join(this.getGeneratedFolder(), "_lookup.json")),
5156
5383
  lookup
5157
5384
  );
5158
5385
  const result = await this._indexAllContent(
@@ -5193,7 +5420,9 @@ var Database = class {
5193
5420
  collections[collection]
5194
5421
  );
5195
5422
  }
5196
- await _deleteIndexContent(this, nonCollectionPaths, enqueueOps, null);
5423
+ if (nonCollectionPaths.length) {
5424
+ await _deleteIndexContent(this, nonCollectionPaths, enqueueOps, null);
5425
+ }
5197
5426
  });
5198
5427
  while (operations.length) {
5199
5428
  await this.level.batch(operations.splice(0, 25));
@@ -5244,6 +5473,11 @@ var Database = class {
5244
5473
  );
5245
5474
  const item = await rootSublevel.get(itemKey);
5246
5475
  if (item) {
5476
+ const folderTreeBuilder = new FolderTreeBuilder();
5477
+ const folderKey = folderTreeBuilder.update(
5478
+ filepath,
5479
+ collection.path || ""
5480
+ );
5247
5481
  await this.level.batch([
5248
5482
  ...makeIndexOpsForDocument(
5249
5483
  filepath,
@@ -5253,6 +5487,14 @@ var Database = class {
5253
5487
  "del",
5254
5488
  this.level
5255
5489
  ),
5490
+ ...makeIndexOpsForDocument(
5491
+ filepath,
5492
+ `${collection.name}_${folderKey}`,
5493
+ collectionIndexDefinitions,
5494
+ item,
5495
+ "del",
5496
+ this.level
5497
+ ),
5256
5498
  {
5257
5499
  type: "del",
5258
5500
  key: itemKey,
@@ -5284,17 +5526,17 @@ var Database = class {
5284
5526
  const documentPaths = await this.bridge.glob(normalPath, format);
5285
5527
  const matches = this.tinaSchema.getMatches({ collection });
5286
5528
  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);
5529
+ filteredPaths.forEach((path5) => {
5530
+ if (filesSeen.has(path5)) {
5531
+ filesSeen.get(path5).push(collection.name);
5532
+ duplicateFiles.add(path5);
5291
5533
  } else {
5292
- filesSeen.set(path4, [collection.name]);
5534
+ filesSeen.set(path5, [collection.name]);
5293
5535
  }
5294
5536
  });
5295
- duplicateFiles.forEach((path4) => {
5537
+ duplicateFiles.forEach((path5) => {
5296
5538
  warnings.push(
5297
- `"${path4}" Found in multiple collections: ${filesSeen.get(path4).map((collection2) => `"${collection2}"`).join(
5539
+ `"${path5}" Found in multiple collections: ${filesSeen.get(path5).map((collection2) => `"${collection2}"`).join(
5298
5540
  ", "
5299
5541
  )}. This can cause unexpected behavior. We recommend updating the \`match\` property of those collections so that each file is in only one collection.
5300
5542
  This will be an error in the future. See https://tina.io/docs/errors/file-in-mutpliple-collections/
@@ -5312,7 +5554,7 @@ This will be an error in the future. See https://tina.io/docs/errors/file-in-mut
5312
5554
  if (!this.bridge) {
5313
5555
  throw new Error("No bridge configured");
5314
5556
  }
5315
- const lookupPath = path2.join(this.getGeneratedFolder(), `_lookup.json`);
5557
+ const lookupPath = path3.join(this.getGeneratedFolder(), `_lookup.json`);
5316
5558
  let lookupMap;
5317
5559
  try {
5318
5560
  lookupMap = JSON.parse(await this.bridge.get(normalizePath(lookupPath)));
@@ -5430,24 +5672,27 @@ function hasOwnProperty(obj, prop) {
5430
5672
  }
5431
5673
  var _indexContent = async (database, level, documentPaths, enqueueOps, collection) => {
5432
5674
  let collectionIndexDefinitions;
5675
+ let collectionPath;
5433
5676
  if (collection) {
5434
5677
  const indexDefinitions = await database.getIndexDefinitions(level);
5435
5678
  collectionIndexDefinitions = indexDefinitions?.[collection.name];
5436
5679
  if (!collectionIndexDefinitions) {
5437
5680
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
5438
5681
  }
5682
+ collectionPath = collection.path;
5439
5683
  }
5440
5684
  const tinaSchema = await database.getSchema();
5441
5685
  let templateInfo = null;
5442
5686
  if (collection) {
5443
5687
  templateInfo = await tinaSchema.getTemplatesForCollectable(collection);
5444
5688
  }
5689
+ const folderTreeBuilder = new FolderTreeBuilder();
5445
5690
  await sequential(documentPaths, async (filepath) => {
5446
5691
  try {
5447
5692
  const dataString = await database.bridge.get(normalizePath(filepath));
5448
5693
  const data = parseFile(
5449
5694
  dataString,
5450
- path2.extname(filepath),
5695
+ path3.extname(filepath),
5451
5696
  (yup3) => yup3.object({}),
5452
5697
  {
5453
5698
  frontmatterDelimiters: collection?.frontmatterDelimiters,
@@ -5462,6 +5707,10 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
5462
5707
  return;
5463
5708
  }
5464
5709
  const normalizedPath = normalizePath(filepath);
5710
+ const folderKey = folderTreeBuilder.update(
5711
+ normalizedPath,
5712
+ collectionPath || ""
5713
+ );
5465
5714
  const aliasedData = templateInfo ? replaceNameOverrides(template, data) : data;
5466
5715
  await enqueueOps([
5467
5716
  ...makeIndexOpsForDocument(
@@ -5472,6 +5721,14 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
5472
5721
  "put",
5473
5722
  level
5474
5723
  ),
5724
+ ...makeIndexOpsForDocument(
5725
+ normalizedPath,
5726
+ `${collection?.name}_${folderKey}`,
5727
+ collectionIndexDefinitions,
5728
+ aliasedData,
5729
+ "put",
5730
+ level
5731
+ ),
5475
5732
  {
5476
5733
  type: "put",
5477
5734
  key: normalizedPath,
@@ -5491,8 +5748,22 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
5491
5748
  });
5492
5749
  }
5493
5750
  });
5751
+ if (collection) {
5752
+ await enqueueOps(
5753
+ makeFolderOpsForCollection(
5754
+ folderTreeBuilder.tree,
5755
+ collection,
5756
+ collectionIndexDefinitions,
5757
+ "put",
5758
+ level
5759
+ )
5760
+ );
5761
+ }
5494
5762
  };
5495
- var _deleteIndexContent = async (database, documentPaths, enequeueOps, collection) => {
5763
+ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection) => {
5764
+ if (!documentPaths.length) {
5765
+ return;
5766
+ }
5496
5767
  let collectionIndexDefinitions;
5497
5768
  if (collection) {
5498
5769
  const indexDefinitions = await database.getIndexDefinitions(database.level);
@@ -5501,20 +5772,42 @@ var _deleteIndexContent = async (database, documentPaths, enequeueOps, collectio
5501
5772
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
5502
5773
  }
5503
5774
  }
5775
+ const tinaSchema = await database.getSchema();
5776
+ let templateInfo = null;
5777
+ if (collection) {
5778
+ templateInfo = await tinaSchema.getTemplatesForCollectable(collection);
5779
+ }
5504
5780
  const rootLevel = database.level.sublevel(
5505
5781
  CONTENT_ROOT_PREFIX,
5506
5782
  SUBLEVEL_OPTIONS
5507
5783
  );
5784
+ const folderTreeBuilder = new FolderTreeBuilder();
5508
5785
  await sequential(documentPaths, async (filepath) => {
5509
5786
  const itemKey = normalizePath(filepath);
5510
5787
  const item = await rootLevel.get(itemKey);
5511
5788
  if (item) {
5512
- await enequeueOps([
5789
+ const folderKey = folderTreeBuilder.update(
5790
+ itemKey,
5791
+ collection?.path || ""
5792
+ );
5793
+ const aliasedData = templateInfo ? replaceNameOverrides(
5794
+ getTemplateForFile(templateInfo, item),
5795
+ item
5796
+ ) : item;
5797
+ await enqueueOps([
5513
5798
  ...makeIndexOpsForDocument(
5514
5799
  itemKey,
5515
5800
  collection.name,
5516
5801
  collectionIndexDefinitions,
5517
- item,
5802
+ aliasedData,
5803
+ "del",
5804
+ database.level
5805
+ ),
5806
+ ...makeIndexOpsForDocument(
5807
+ itemKey,
5808
+ `${collection?.name}_${folderKey}`,
5809
+ collectionIndexDefinitions,
5810
+ aliasedData,
5518
5811
  "del",
5519
5812
  database.level
5520
5813
  ),
@@ -5522,6 +5815,17 @@ var _deleteIndexContent = async (database, documentPaths, enequeueOps, collectio
5522
5815
  ]);
5523
5816
  }
5524
5817
  });
5818
+ if (collectionIndexDefinitions) {
5819
+ await enqueueOps(
5820
+ makeFolderOpsForCollection(
5821
+ folderTreeBuilder.tree,
5822
+ collection,
5823
+ collectionIndexDefinitions,
5824
+ "del",
5825
+ database.level
5826
+ )
5827
+ );
5828
+ }
5525
5829
  };
5526
5830
  var getTemplateForFile = (templateInfo, data) => {
5527
5831
  if (templateInfo.type === "object") {
@@ -5569,7 +5873,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
5569
5873
  // src/database/bridge/filesystem.ts
5570
5874
  import fs from "fs-extra";
5571
5875
  import fg from "fast-glob";
5572
- import path3 from "path";
5876
+ import path4 from "path";
5573
5877
  import normalize from "normalize-path";
5574
5878
  var FilesystemBridge = class {
5575
5879
  constructor(rootPath, outputPath) {
@@ -5577,9 +5881,9 @@ var FilesystemBridge = class {
5577
5881
  this.outputPath = outputPath || rootPath;
5578
5882
  }
5579
5883
  async glob(pattern, extension) {
5580
- const basePath = path3.join(this.outputPath, ...pattern.split("/"));
5884
+ const basePath = path4.join(this.outputPath, ...pattern.split("/"));
5581
5885
  const items = await fg(
5582
- path3.join(basePath, "**", `/*${extension}`).replace(/\\/g, "/"),
5886
+ path4.join(basePath, "**", `/*${extension}`).replace(/\\/g, "/"),
5583
5887
  {
5584
5888
  dot: true
5585
5889
  }
@@ -5593,10 +5897,10 @@ var FilesystemBridge = class {
5593
5897
  return true;
5594
5898
  }
5595
5899
  async delete(filepath) {
5596
- await fs.remove(path3.join(this.outputPath, filepath));
5900
+ await fs.remove(path4.join(this.outputPath, filepath));
5597
5901
  }
5598
5902
  async get(filepath) {
5599
- return fs.readFileSync(path3.join(this.outputPath, filepath)).toString();
5903
+ return fs.readFileSync(path4.join(this.outputPath, filepath)).toString();
5600
5904
  }
5601
5905
  async putConfig(filepath, data) {
5602
5906
  if (this.rootPath !== this.outputPath) {
@@ -5608,7 +5912,7 @@ var FilesystemBridge = class {
5608
5912
  }
5609
5913
  async put(filepath, data, basePathOverride) {
5610
5914
  const basePath = basePathOverride || this.outputPath;
5611
- await fs.outputFileSync(path3.join(basePath, filepath), data);
5915
+ await fs.outputFileSync(path4.join(basePath, filepath), data);
5612
5916
  }
5613
5917
  };
5614
5918
  var AuditFileSystemBridge = class extends FilesystemBridge {
@@ -5689,7 +5993,7 @@ var IsomorphicBridge = class {
5689
5993
  async listEntries({
5690
5994
  pattern,
5691
5995
  entry,
5692
- path: path4,
5996
+ path: path5,
5693
5997
  results
5694
5998
  }) {
5695
5999
  const treeResult = await git.readTree({
@@ -5699,7 +6003,7 @@ var IsomorphicBridge = class {
5699
6003
  });
5700
6004
  const children = [];
5701
6005
  for (const childEntry of treeResult.tree) {
5702
- const childPath = path4 ? `${path4}/${childEntry.path}` : childEntry.path;
6006
+ const childPath = path5 ? `${path5}/${childEntry.path}` : childEntry.path;
5703
6007
  if (childEntry.type === "tree") {
5704
6008
  children.push(childEntry);
5705
6009
  } else {
@@ -5709,7 +6013,7 @@ var IsomorphicBridge = class {
5709
6013
  }
5710
6014
  }
5711
6015
  for (const childEntry of children) {
5712
- const childPath = path4 ? `${path4}/${childEntry.path}` : childEntry.path;
6016
+ const childPath = path5 ? `${path5}/${childEntry.path}` : childEntry.path;
5713
6017
  await this.listEntries({
5714
6018
  pattern,
5715
6019
  entry: childEntry,
@@ -5718,17 +6022,17 @@ var IsomorphicBridge = class {
5718
6022
  });
5719
6023
  }
5720
6024
  }
5721
- async resolvePathEntries(path4, ref) {
5722
- let pathParts = path4.split("/");
6025
+ async resolvePathEntries(path5, ref) {
6026
+ let pathParts = path5.split("/");
5723
6027
  const result = await git.walk({
5724
6028
  ...this.isomorphicConfig,
5725
6029
  map: async (filepath, [head]) => {
5726
6030
  if (head._fullpath === ".") {
5727
6031
  return head;
5728
6032
  }
5729
- if (path4.startsWith(filepath)) {
5730
- if (dirname(path4) === dirname(filepath)) {
5731
- if (path4 === filepath) {
6033
+ if (path5.startsWith(filepath)) {
6034
+ if (dirname(path5) === dirname(filepath)) {
6035
+ if (path5 === filepath) {
5732
6036
  return head;
5733
6037
  }
5734
6038
  } else {
@@ -5748,7 +6052,7 @@ var IsomorphicBridge = class {
5748
6052
  }
5749
6053
  return { pathParts, pathEntries };
5750
6054
  }
5751
- async updateTreeHierarchy(existingOid, updatedOid, path4, type, pathEntries, pathParts) {
6055
+ async updateTreeHierarchy(existingOid, updatedOid, path5, type, pathEntries, pathParts) {
5752
6056
  const lastIdx = pathEntries.length - 1;
5753
6057
  const parentEntry = pathEntries[lastIdx];
5754
6058
  const parentPath = pathParts[lastIdx];
@@ -5763,7 +6067,7 @@ var IsomorphicBridge = class {
5763
6067
  cache: this.cache
5764
6068
  });
5765
6069
  tree = existingOid ? treeResult.tree.map((entry) => {
5766
- if (entry.path === path4) {
6070
+ if (entry.path === path5) {
5767
6071
  entry.oid = updatedOid;
5768
6072
  }
5769
6073
  return entry;
@@ -5772,7 +6076,7 @@ var IsomorphicBridge = class {
5772
6076
  {
5773
6077
  oid: updatedOid,
5774
6078
  type,
5775
- path: path4,
6079
+ path: path5,
5776
6080
  mode
5777
6081
  }
5778
6082
  ];
@@ -5781,7 +6085,7 @@ var IsomorphicBridge = class {
5781
6085
  {
5782
6086
  oid: updatedOid,
5783
6087
  type,
5784
- path: path4,
6088
+ path: path5,
5785
6089
  mode
5786
6090
  }
5787
6091
  ];
@@ -5882,7 +6186,7 @@ var IsomorphicBridge = class {
5882
6186
  path: parentPath,
5883
6187
  results
5884
6188
  });
5885
- return results.map((path4) => this.unqualifyPath(path4)).filter((path4) => path4.endsWith(extension));
6189
+ return results.map((path5) => this.unqualifyPath(path5)).filter((path5) => path5.endsWith(extension));
5886
6190
  }
5887
6191
  supportsBuilding() {
5888
6192
  return true;