@tinacms/graphql 0.0.0-bf8b9b7-20251204000148 → 0.0.0-c19d29e-20251224001156

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -45,8 +45,8 @@ var btoa = (string2) => {
45
45
  var lastItem = (arr) => {
46
46
  return arr[arr.length - 1];
47
47
  };
48
- var get = (obj, path7, defaultValue = void 0) => {
49
- const travel = (regexp) => String.prototype.split.call(path7, regexp).filter(Boolean).reduce(
48
+ var get = (obj, path8, defaultValue = void 0) => {
49
+ const travel = (regexp) => String.prototype.split.call(path8, regexp).filter(Boolean).reduce(
50
50
  (res, key) => res !== null && res !== void 0 ? res[key] : res,
51
51
  obj
52
52
  );
@@ -3026,7 +3026,7 @@ var validateField = async (field) => {
3026
3026
  var package_default = {
3027
3027
  name: "@tinacms/graphql",
3028
3028
  type: "module",
3029
- version: "2.0.0",
3029
+ version: "2.0.3",
3030
3030
  main: "dist/index.js",
3031
3031
  module: "./dist/index.js",
3032
3032
  files: [
@@ -3308,7 +3308,12 @@ var _buildSchema = async (builder, tinaSchema) => {
3308
3308
  };
3309
3309
 
3310
3310
  // src/resolve.ts
3311
- import { graphql, buildASTSchema, getNamedType, GraphQLError as GraphQLError4 } from "graphql";
3311
+ import {
3312
+ graphql,
3313
+ buildASTSchema,
3314
+ getNamedType,
3315
+ GraphQLError as GraphQLError4
3316
+ } from "graphql";
3312
3317
 
3313
3318
  // src/resolver/index.ts
3314
3319
  import path3 from "path";
@@ -3504,6 +3509,56 @@ var matterEngines = {
3504
3509
  toml: {
3505
3510
  parse: (val) => toml.parse(val),
3506
3511
  stringify: (val) => toml.stringify(val)
3512
+ },
3513
+ // Disable JavaScript and CoffeeScript execution to prevent code execution vulnerability (CVE)
3514
+ // gray-matter executes JS/Coffee code by default, which is a security risk
3515
+ js: {
3516
+ parse: () => {
3517
+ throw new Error(
3518
+ "JavaScript execution in frontmatter is not allowed for security reasons"
3519
+ );
3520
+ },
3521
+ stringify: () => {
3522
+ throw new Error(
3523
+ "JavaScript execution in frontmatter is not allowed for security reasons"
3524
+ );
3525
+ }
3526
+ },
3527
+ javascript: {
3528
+ parse: () => {
3529
+ throw new Error(
3530
+ "JavaScript execution in frontmatter is not allowed for security reasons"
3531
+ );
3532
+ },
3533
+ stringify: () => {
3534
+ throw new Error(
3535
+ "JavaScript execution in frontmatter is not allowed for security reasons"
3536
+ );
3537
+ }
3538
+ },
3539
+ coffee: {
3540
+ parse: () => {
3541
+ throw new Error(
3542
+ "CoffeeScript execution in frontmatter is not allowed for security reasons"
3543
+ );
3544
+ },
3545
+ stringify: () => {
3546
+ throw new Error(
3547
+ "CoffeeScript execution in frontmatter is not allowed for security reasons"
3548
+ );
3549
+ }
3550
+ },
3551
+ coffeescript: {
3552
+ parse: () => {
3553
+ throw new Error(
3554
+ "CoffeeScript execution in frontmatter is not allowed for security reasons"
3555
+ );
3556
+ },
3557
+ stringify: () => {
3558
+ throw new Error(
3559
+ "CoffeeScript execution in frontmatter is not allowed for security reasons"
3560
+ );
3561
+ }
3507
3562
  }
3508
3563
  };
3509
3564
  var stringifyFile = (content, format, keepTemplateKey, markdownParseConfig) => {
@@ -3597,17 +3652,17 @@ var scanAllContent = async (tinaSchema, bridge, callback) => {
3597
3652
  const documentPaths = await bridge.glob(normalPath, format);
3598
3653
  const matches = tinaSchema.getMatches({ collection });
3599
3654
  const filteredPaths = matches.length > 0 ? micromatch(documentPaths, matches) : documentPaths;
3600
- filteredPaths.forEach((path7) => {
3601
- if (filesSeen.has(path7)) {
3602
- filesSeen.get(path7).push(collection.name);
3603
- duplicateFiles.add(path7);
3655
+ filteredPaths.forEach((path8) => {
3656
+ if (filesSeen.has(path8)) {
3657
+ filesSeen.get(path8).push(collection.name);
3658
+ duplicateFiles.add(path8);
3604
3659
  } else {
3605
- filesSeen.set(path7, [collection.name]);
3660
+ filesSeen.set(path8, [collection.name]);
3606
3661
  }
3607
3662
  });
3608
- duplicateFiles.forEach((path7) => {
3663
+ duplicateFiles.forEach((path8) => {
3609
3664
  warnings.push(
3610
- `"${path7}" Found in multiple collections: ${filesSeen.get(path7).map((collection2) => `"${collection2}"`).join(
3665
+ `"${path8}" Found in multiple collections: ${filesSeen.get(path8).map((collection2) => `"${collection2}"`).join(
3611
3666
  ", "
3612
3667
  )}. This can cause unexpected behavior. We recommend updating the \`match\` property of those collections so that each file is in only one collection.
3613
3668
  This will be an error in the future. See https://tina.io/docs/errors/file-in-mutpliple-collections/
@@ -4170,9 +4225,9 @@ var makeFilterSuffixes = (filterChain, index) => {
4170
4225
  }
4171
4226
  };
4172
4227
  var FOLDER_ROOT = "~";
4173
- var stripCollectionFromPath = (collectionPath, path7) => {
4228
+ var stripCollectionFromPath = (collectionPath, path8) => {
4174
4229
  const collectionPathParts = collectionPath.split("/");
4175
- const pathParts = path7.split("/");
4230
+ const pathParts = path8.split("/");
4176
4231
  const strippedPathParts = pathParts.slice(collectionPathParts.length);
4177
4232
  return strippedPathParts.join("/");
4178
4233
  };
@@ -4228,13 +4283,13 @@ var makeFolderOpsForCollection = (folderTree, collection, indexDefinitions, opTy
4228
4283
  SUBLEVEL_OPTIONS
4229
4284
  );
4230
4285
  let folderSortingIdx = 0;
4231
- for (const path7 of Array.from(folder).sort()) {
4286
+ for (const path8 of Array.from(folder).sort()) {
4232
4287
  for (const [sort] of Object.entries(indexDefinitions)) {
4233
4288
  const indexSublevel = folderCollectionSublevel.sublevel(
4234
4289
  sort,
4235
4290
  SUBLEVEL_OPTIONS
4236
4291
  );
4237
- const subFolderKey = sha.hex(path7);
4292
+ const subFolderKey = sha.hex(path8);
4238
4293
  if (sort === DEFAULT_COLLECTION_SORT_KEY) {
4239
4294
  result.push({
4240
4295
  type: opType,
@@ -4316,8 +4371,8 @@ var makeRefOpsForDocument = (filepath, collection, references, data, opType, lev
4316
4371
  SUBLEVEL_OPTIONS
4317
4372
  );
4318
4373
  const references2 = {};
4319
- for (const path7 of referencePaths) {
4320
- const ref = JSONPath({ path: path7, json: data });
4374
+ for (const path8 of referencePaths) {
4375
+ const ref = JSONPath({ path: path8, json: data });
4321
4376
  if (!ref) {
4322
4377
  continue;
4323
4378
  }
@@ -4327,24 +4382,24 @@ var makeRefOpsForDocument = (filepath, collection, references, data, opType, lev
4327
4382
  continue;
4328
4383
  }
4329
4384
  if (references2[r]) {
4330
- references2[r].push(path7);
4385
+ references2[r].push(path8);
4331
4386
  } else {
4332
- references2[r] = [path7];
4387
+ references2[r] = [path8];
4333
4388
  }
4334
4389
  }
4335
4390
  } else {
4336
4391
  if (references2[ref]) {
4337
- references2[ref].push(path7);
4392
+ references2[ref].push(path8);
4338
4393
  } else {
4339
- references2[ref] = [path7];
4394
+ references2[ref] = [path8];
4340
4395
  }
4341
4396
  }
4342
4397
  }
4343
4398
  for (const ref of Object.keys(references2)) {
4344
- for (const path7 of references2[ref]) {
4399
+ for (const path8 of references2[ref]) {
4345
4400
  result.push({
4346
4401
  type: opType,
4347
- key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4402
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path8}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4348
4403
  sublevel: refSublevel,
4349
4404
  value: opType === "put" ? {} : void 0
4350
4405
  });
@@ -4611,9 +4666,9 @@ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, s
4611
4666
  return value;
4612
4667
  }
4613
4668
  };
4614
- var cleanUpSlashes = (path7) => {
4615
- if (path7) {
4616
- return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
4669
+ var cleanUpSlashes = (path8) => {
4670
+ if (path8) {
4671
+ return `/${path8.replace(/^\/+|\/+$/gm, "")}`;
4617
4672
  }
4618
4673
  return "";
4619
4674
  };
@@ -4823,17 +4878,17 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4823
4878
  throw e;
4824
4879
  }
4825
4880
  };
4826
- var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4881
+ var updateObjectWithJsonPath = (obj, path8, oldValue, newValue) => {
4827
4882
  let updated = false;
4828
- if (!path7.includes(".") && !path7.includes("[")) {
4829
- if (path7 in obj && obj[path7] === oldValue) {
4830
- obj[path7] = newValue;
4883
+ if (!path8.includes(".") && !path8.includes("[")) {
4884
+ if (path8 in obj && obj[path8] === oldValue) {
4885
+ obj[path8] = newValue;
4831
4886
  updated = true;
4832
4887
  }
4833
4888
  return { object: obj, updated };
4834
4889
  }
4835
- const parentPath = path7.replace(/\.[^.\[\]]+$/, "");
4836
- const keyToUpdate = path7.match(/[^.\[\]]+$/)[0];
4890
+ const parentPath = path8.replace(/\.[^.\[\]]+$/, "");
4891
+ const keyToUpdate = path8.match(/[^.\[\]]+$/)[0];
4837
4892
  const parents = JSONPath2({
4838
4893
  path: parentPath,
4839
4894
  json: obj,
@@ -4863,7 +4918,7 @@ var Resolver = class {
4863
4918
  database;
4864
4919
  tinaSchema;
4865
4920
  isAudit;
4866
- resolveCollection = async (args, collectionName, hasDocuments) => {
4921
+ resolveCollection = async (_args, collectionName, hasDocuments) => {
4867
4922
  const collection = this.tinaSchema.getCollection(collectionName);
4868
4923
  const extraFields = {};
4869
4924
  return {
@@ -5030,38 +5085,296 @@ var Resolver = class {
5030
5085
  }
5031
5086
  }
5032
5087
  };
5033
- createResolveDocument = async ({
5034
- collection,
5035
- realPath,
5036
- args,
5037
- isAddPendingDocument
5088
+ resolveAddPendingDocument = async ({
5089
+ collectionName,
5090
+ relativePath,
5091
+ templateName
5038
5092
  }) => {
5039
- if (isAddPendingDocument === true) {
5040
- const templateInfo = this.tinaSchema.getTemplatesForCollectable(collection);
5041
- switch (templateInfo.type) {
5042
- case "object":
5043
- await this.database.addPendingDocument(realPath, {});
5044
- break;
5045
- case "union":
5046
- const templateString = args.template;
5047
- const template = templateInfo.templates.find(
5048
- (template2) => lastItem(template2.namespace) === templateString
5093
+ const collection = this.getCollectionWithName(collectionName);
5094
+ const realPath = path3.join(collection.path, relativePath);
5095
+ const alreadyExists = await this.database.documentExists(realPath);
5096
+ if (alreadyExists) {
5097
+ throw new Error(`Unable to add document, ${realPath} already exists`);
5098
+ }
5099
+ const templateInfo = this.tinaSchema.getTemplatesForCollectable(collection);
5100
+ switch (templateInfo.type) {
5101
+ case "object":
5102
+ await this.database.addPendingDocument(realPath, {});
5103
+ break;
5104
+ case "union":
5105
+ if (!templateName) {
5106
+ throw new Error(
5107
+ `Must specify a template when creating content for a collection with multiple templates. Possible templates are: ${templateInfo.templates.map((t) => lastItem(t.namespace)).join(" ")}`
5049
5108
  );
5050
- if (!args.template) {
5051
- throw new Error(
5052
- `Must specify a template when creating content for a collection with multiple templates. Possible templates are: ${templateInfo.templates.map((t) => lastItem(t.namespace)).join(" ")}`
5109
+ }
5110
+ const template = templateInfo.templates.find(
5111
+ (template2) => lastItem(template2.namespace) === templateName
5112
+ );
5113
+ if (!template) {
5114
+ throw new Error(
5115
+ `Expected to find template named ${templateName} in collection "${collection.name}" but none was found. Possible templates are: ${templateInfo.templates.map((t) => lastItem(t.namespace)).join(" ")}`
5116
+ );
5117
+ }
5118
+ await this.database.addPendingDocument(realPath, {
5119
+ _template: lastItem(template.namespace)
5120
+ });
5121
+ }
5122
+ return this.getDocument(realPath);
5123
+ };
5124
+ /**
5125
+ * Returns top-level fields which are not defined in the collection, so their
5126
+ * values are not eliminated from Tina when new values are saved
5127
+ */
5128
+ resolveLegacyValues = (oldDoc, collection) => {
5129
+ const legacyValues = {};
5130
+ Object.entries(oldDoc).forEach(([key, value]) => {
5131
+ const reservedKeys = [
5132
+ "$_body",
5133
+ "_collection",
5134
+ "_keepTemplateKey",
5135
+ "_template",
5136
+ "_relativePath",
5137
+ "_id"
5138
+ ];
5139
+ if (reservedKeys.includes(key)) {
5140
+ return;
5141
+ }
5142
+ if (oldDoc._template && collection.templates) {
5143
+ const template = collection.templates?.find(
5144
+ ({ name }) => name === oldDoc._template
5145
+ );
5146
+ if (template) {
5147
+ if (!template.fields.find(({ name }) => name === key)) {
5148
+ legacyValues[key] = value;
5149
+ }
5150
+ }
5151
+ }
5152
+ if (oldDoc._collection && collection.fields) {
5153
+ if (!collection.fields.find(({ name }) => name === key)) {
5154
+ legacyValues[key] = value;
5155
+ }
5156
+ }
5157
+ });
5158
+ return legacyValues;
5159
+ };
5160
+ getCollectionWithName = (collectionName) => {
5161
+ const collectionNames = this.tinaSchema.getCollections().map((item) => item.name);
5162
+ if (!collectionNames.includes(collectionName)) {
5163
+ throw new Error(
5164
+ `"collection" must be one of: [${collectionNames.join(
5165
+ ", "
5166
+ )}] but got ${collectionName}`
5167
+ );
5168
+ }
5169
+ return this.tinaSchema.getCollection(collectionName);
5170
+ };
5171
+ /*
5172
+ * Used for getDocument, get<Collection>Document.
5173
+ */
5174
+ resolveRetrievedDocument = async ({
5175
+ collectionName,
5176
+ relativePath
5177
+ }) => {
5178
+ const collection = this.getCollectionWithName(collectionName);
5179
+ const realPath = path3.join(collection.path, relativePath);
5180
+ return this.getDocument(realPath, {
5181
+ collection,
5182
+ checkReferences: true
5183
+ });
5184
+ };
5185
+ /*
5186
+ * Used for createFolder, create<Collection>Folder
5187
+ */
5188
+ resolveCreateFolder = async ({
5189
+ collectionName,
5190
+ relativePath
5191
+ }) => {
5192
+ const collection = this.getCollectionWithName(collectionName);
5193
+ const realPath = path3.join(
5194
+ collection.path,
5195
+ relativePath,
5196
+ `.gitkeep.${collection.format || "md"}`
5197
+ );
5198
+ const alreadyExists = await this.database.documentExists(realPath);
5199
+ if (alreadyExists) {
5200
+ throw new Error(`Unable to add folder, ${realPath} already exists`);
5201
+ }
5202
+ await this.database.put(
5203
+ realPath,
5204
+ { _is_tina_folder_placeholder: true },
5205
+ collection.name
5206
+ );
5207
+ return this.getDocument(realPath);
5208
+ };
5209
+ resolveCreateDocument = async ({
5210
+ collectionName,
5211
+ relativePath,
5212
+ body
5213
+ }) => {
5214
+ const collection = this.getCollectionWithName(collectionName);
5215
+ const realPath = path3.join(collection.path, relativePath);
5216
+ const alreadyExists = await this.database.documentExists(realPath);
5217
+ if (alreadyExists) {
5218
+ throw new Error(`Unable to add document, ${realPath} already exists`);
5219
+ }
5220
+ const params = await this.buildObjectMutations(body, collection);
5221
+ await this.database.put(realPath, params, collection.name);
5222
+ return this.getDocument(realPath);
5223
+ };
5224
+ resolveUpdateDocument = async ({
5225
+ collectionName,
5226
+ relativePath,
5227
+ newRelativePath,
5228
+ newBody
5229
+ }) => {
5230
+ const collection = this.getCollectionWithName(collectionName);
5231
+ const realPath = path3.join(collection.path, relativePath);
5232
+ const alreadyExists = await this.database.documentExists(realPath);
5233
+ if (!alreadyExists) {
5234
+ throw new Error(`Unable to update document, ${realPath} does not exist`);
5235
+ }
5236
+ const doc = await this.getDocument(realPath);
5237
+ if (newRelativePath) {
5238
+ const newRealPath = path3.join(collection?.path, newRelativePath);
5239
+ if (newRealPath === realPath) {
5240
+ return doc;
5241
+ }
5242
+ await this.database.put(newRealPath, doc._rawData, collection.name);
5243
+ await this.deleteDocument(realPath);
5244
+ const collRefs = await this.findReferences(realPath, collection);
5245
+ for (const [_collection, docsWithRefs] of Object.entries(collRefs)) {
5246
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5247
+ docsWithRefs
5248
+ )) {
5249
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5250
+ let hasUpdate = false;
5251
+ for (const path8 of referencePaths) {
5252
+ const { object: object2, updated } = updateObjectWithJsonPath(
5253
+ docWithRef,
5254
+ path8,
5255
+ realPath,
5256
+ newRealPath
5053
5257
  );
5258
+ docWithRef = object2;
5259
+ hasUpdate = updated || hasUpdate;
5054
5260
  }
5055
- if (!template) {
5056
- throw new Error(
5057
- `Expected to find template named ${templateString} in collection "${collection.name}" but none was found. Possible templates are: ${templateInfo.templates.map((t) => lastItem(t.namespace)).join(" ")}`
5261
+ if (hasUpdate) {
5262
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5263
+ if (!collectionWithRef) {
5264
+ throw new Error(
5265
+ `Unable to find collection for ${pathToDocWithRef}`
5266
+ );
5267
+ }
5268
+ await this.database.put(
5269
+ pathToDocWithRef,
5270
+ docWithRef,
5271
+ collectionWithRef.name
5058
5272
  );
5059
5273
  }
5060
- await this.database.addPendingDocument(realPath, {
5061
- _template: lastItem(template.namespace)
5062
- });
5274
+ }
5063
5275
  }
5064
- return this.getDocument(realPath);
5276
+ return this.getDocument(newRealPath);
5277
+ }
5278
+ if (!newBody) {
5279
+ throw new Error("Body not provided for updated document.");
5280
+ }
5281
+ const params = await this.buildObjectMutations(
5282
+ newBody,
5283
+ collection,
5284
+ doc?._rawData
5285
+ );
5286
+ const legacyValues = this.resolveLegacyValues(
5287
+ doc?._rawData || {},
5288
+ collection
5289
+ );
5290
+ await this.database.put(
5291
+ realPath,
5292
+ { ...legacyValues, ...params },
5293
+ collection.name
5294
+ );
5295
+ return this.getDocument(realPath);
5296
+ };
5297
+ resolveDeleteDocument = async ({
5298
+ collectionName,
5299
+ relativePath
5300
+ }) => {
5301
+ const collection = this.getCollectionWithName(collectionName);
5302
+ const realPath = path3.join(collection.path, relativePath);
5303
+ const alreadyExists = await this.database.documentExists(realPath);
5304
+ if (!alreadyExists) {
5305
+ throw new Error(`Unable to delete document, ${realPath} does not exist`);
5306
+ }
5307
+ const doc = await this.getDocument(realPath);
5308
+ await this.deleteDocument(realPath);
5309
+ if (await this.hasReferences(realPath, collection)) {
5310
+ const collRefs = await this.findReferences(realPath, collection);
5311
+ for (const [_collection, docsWithRefs] of Object.entries(collRefs)) {
5312
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5313
+ docsWithRefs
5314
+ )) {
5315
+ let refDoc = await this.getRaw(pathToDocWithRef);
5316
+ let hasUpdate = false;
5317
+ for (const path8 of referencePaths) {
5318
+ const { object: object2, updated } = updateObjectWithJsonPath(
5319
+ refDoc,
5320
+ path8,
5321
+ realPath,
5322
+ null
5323
+ );
5324
+ refDoc = object2;
5325
+ hasUpdate = updated || hasUpdate;
5326
+ }
5327
+ if (hasUpdate) {
5328
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5329
+ if (!collectionWithRef) {
5330
+ throw new Error(
5331
+ `Unable to find collection for ${pathToDocWithRef}`
5332
+ );
5333
+ }
5334
+ await this.database.put(
5335
+ pathToDocWithRef,
5336
+ refDoc,
5337
+ collectionWithRef.name
5338
+ );
5339
+ }
5340
+ }
5341
+ }
5342
+ }
5343
+ return doc;
5344
+ };
5345
+ resolveAndValidateCollection = ({
5346
+ collectionName,
5347
+ args,
5348
+ isCollectionSpecific
5349
+ }) => {
5350
+ let collectionLookup = collectionName || void 0;
5351
+ if (!collectionLookup && isCollectionSpecific === false) {
5352
+ assertShape(
5353
+ args,
5354
+ (yup3) => yup3.object({
5355
+ params: yup3.object().required()
5356
+ })
5357
+ );
5358
+ collectionLookup = Object.keys(args.params)[0];
5359
+ }
5360
+ const collection = this.getCollectionWithName(collectionLookup);
5361
+ return { collection };
5362
+ };
5363
+ /**
5364
+ * @deprecated - To be removed in next major version.
5365
+ */
5366
+ createResolveDocument = async ({
5367
+ collection,
5368
+ realPath,
5369
+ args,
5370
+ isAddPendingDocument
5371
+ }) => {
5372
+ if (isAddPendingDocument === true) {
5373
+ return this.resolveAddPendingDocument({
5374
+ collectionName: collection.name,
5375
+ relativePath: path3.relative(collection.path, realPath),
5376
+ templateName: args.template
5377
+ });
5065
5378
  }
5066
5379
  const params = await this.buildObjectMutations(
5067
5380
  // @ts-ignore
@@ -5071,6 +5384,9 @@ var Resolver = class {
5071
5384
  await this.database.put(realPath, params, collection.name);
5072
5385
  return this.getDocument(realPath);
5073
5386
  };
5387
+ /**
5388
+ * @deprecated - To be removed in next major version.
5389
+ */
5074
5390
  updateResolveDocument = async ({
5075
5391
  collection,
5076
5392
  realPath,
@@ -5081,6 +5397,9 @@ var Resolver = class {
5081
5397
  const doc = await this.getDocument(realPath);
5082
5398
  const oldDoc = this.resolveLegacyValues(doc?._rawData || {}, collection);
5083
5399
  if (isAddPendingDocument === true) {
5400
+ console.warn(
5401
+ "*** DEPRECATION: isAddPendingDocument functionality will be removed Resolver.updateResolveDocument in a future version. ***"
5402
+ );
5084
5403
  const templateInfo = this.tinaSchema.getTemplatesForCollectable(collection);
5085
5404
  const params2 = this.buildParams(args);
5086
5405
  switch (templateInfo.type) {
@@ -5137,41 +5456,8 @@ var Resolver = class {
5137
5456
  return this.getDocument(realPath);
5138
5457
  };
5139
5458
  /**
5140
- * Returns top-level fields which are not defined in the collection, so their
5141
- * values are not eliminated from Tina when new values are saved
5459
+ * @deprecated
5142
5460
  */
5143
- resolveLegacyValues = (oldDoc, collection) => {
5144
- const legacyValues = {};
5145
- Object.entries(oldDoc).forEach(([key, value]) => {
5146
- const reservedKeys = [
5147
- "$_body",
5148
- "_collection",
5149
- "_keepTemplateKey",
5150
- "_template",
5151
- "_relativePath",
5152
- "_id"
5153
- ];
5154
- if (reservedKeys.includes(key)) {
5155
- return;
5156
- }
5157
- if (oldDoc._template && collection.templates) {
5158
- const template = collection.templates?.find(
5159
- ({ name }) => name === oldDoc._template
5160
- );
5161
- if (template) {
5162
- if (!template.fields.find(({ name }) => name === key)) {
5163
- legacyValues[key] = value;
5164
- }
5165
- }
5166
- }
5167
- if (oldDoc._collection && collection.fields) {
5168
- if (!collection.fields.find(({ name }) => name === key)) {
5169
- legacyValues[key] = value;
5170
- }
5171
- }
5172
- });
5173
- return legacyValues;
5174
- };
5175
5461
  resolveDocument = async ({
5176
5462
  args,
5177
5463
  collection: collectionName,
@@ -5183,169 +5469,76 @@ var Resolver = class {
5183
5469
  isCollectionSpecific,
5184
5470
  isUpdateName
5185
5471
  }) => {
5186
- let collectionLookup = collectionName || void 0;
5187
- if (!collectionLookup && isCollectionSpecific === false) {
5188
- collectionLookup = Object.keys(args.params)[0];
5189
- }
5190
- const collectionNames = this.tinaSchema.getCollections().map((item) => item.name);
5191
- assertShape(
5192
- collectionLookup,
5193
- (yup3) => {
5194
- return yup3.mixed().oneOf(collectionNames);
5195
- },
5196
- `"collection" must be one of: [${collectionNames.join(
5197
- ", "
5198
- )}] but got ${collectionLookup}`
5199
- );
5472
+ const { collection } = this.resolveAndValidateCollection({
5473
+ collectionName,
5474
+ args,
5475
+ isCollectionSpecific
5476
+ });
5200
5477
  assertShape(
5201
5478
  args,
5202
5479
  (yup3) => yup3.object({ relativePath: yup3.string().required() })
5203
5480
  );
5204
- const collection = await this.tinaSchema.getCollection(collectionLookup);
5205
- let realPath = path3.join(collection?.path, args.relativePath);
5206
- if (isFolderCreation) {
5207
- realPath = `${realPath}/.gitkeep.${collection.format || "md"}`;
5208
- }
5209
- const alreadyExists = await this.database.documentExists(realPath);
5210
5481
  if (isMutation) {
5211
5482
  if (isCreation) {
5212
- if (alreadyExists === true) {
5213
- throw new Error(`Unable to add document, ${realPath} already exists`);
5483
+ if (isAddPendingDocument) {
5484
+ assertShape(
5485
+ args,
5486
+ (yup3) => yup3.object({ template: yup3.string() })
5487
+ );
5488
+ return this.resolveAddPendingDocument({
5489
+ collectionName: collection.name,
5490
+ relativePath: args.relativePath,
5491
+ templateName: args.template || ""
5492
+ });
5493
+ } else {
5494
+ assertShape(
5495
+ args,
5496
+ (yup3) => yup3.object({ params: yup3.object().required() })
5497
+ );
5498
+ return this.resolveCreateDocument({
5499
+ collectionName: collection.name,
5500
+ relativePath: args.relativePath,
5501
+ body: args.params[collection.name]
5502
+ });
5214
5503
  }
5215
- return this.createResolveDocument({
5504
+ } else if (isFolderCreation) {
5505
+ return this.resolveCreateFolder({
5506
+ collectionName: collection.name,
5507
+ relativePath: args.relativePath
5508
+ });
5509
+ } else if (isDeletion) {
5510
+ return this.resolveDeleteDocument({
5511
+ collectionName: collection.name,
5512
+ relativePath: args.relativePath
5513
+ });
5514
+ } else if (isUpdateName) {
5515
+ assertShape(
5516
+ args,
5517
+ (yup3) => yup3.object({
5518
+ params: yup3.object({ relativePath: yup3.string().required() }).required()
5519
+ })
5520
+ );
5521
+ const realPath = path3.join(collection.path, args.relativePath);
5522
+ return this.updateResolveDocument({
5216
5523
  collection,
5217
5524
  realPath,
5218
5525
  args,
5219
- isAddPendingDocument
5526
+ isAddPendingDocument,
5527
+ isCollectionSpecific
5220
5528
  });
5221
- } else if (isFolderCreation) {
5222
- if (alreadyExists === true) {
5223
- throw new Error(`Unable to add folder, ${realPath} already exists`);
5224
- }
5225
- await this.database.put(
5226
- realPath,
5227
- { _is_tina_folder_placeholder: true },
5228
- collection.name
5229
- );
5230
- return this.getDocument(realPath);
5231
- }
5232
- if (!alreadyExists) {
5233
- if (isDeletion) {
5234
- throw new Error(
5235
- `Unable to delete document, ${realPath} does not exist`
5236
- );
5237
- }
5238
- if (isUpdateName) {
5239
- throw new Error(
5240
- `Unable to update document, ${realPath} does not exist`
5241
- );
5242
- }
5243
- }
5244
- if (isDeletion) {
5245
- const doc = await this.getDocument(realPath);
5246
- await this.deleteDocument(realPath);
5247
- if (await this.hasReferences(realPath, collection)) {
5248
- const collRefs = await this.findReferences(realPath, collection);
5249
- for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5250
- for (const [pathToDocWithRef, referencePaths] of Object.entries(
5251
- docsWithRefs
5252
- )) {
5253
- let refDoc = await this.getRaw(pathToDocWithRef);
5254
- let hasUpdate = false;
5255
- for (const path7 of referencePaths) {
5256
- const { object: object2, updated } = updateObjectWithJsonPath(
5257
- refDoc,
5258
- path7,
5259
- realPath,
5260
- null
5261
- );
5262
- refDoc = object2;
5263
- hasUpdate = updated || hasUpdate;
5264
- }
5265
- if (hasUpdate) {
5266
- const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5267
- if (!collectionWithRef) {
5268
- throw new Error(
5269
- `Unable to find collection for ${pathToDocWithRef}`
5270
- );
5271
- }
5272
- await this.database.put(
5273
- pathToDocWithRef,
5274
- refDoc,
5275
- collectionWithRef.name
5276
- );
5277
- }
5278
- }
5279
- }
5280
- }
5281
- return doc;
5282
- }
5283
- if (isUpdateName) {
5529
+ } else {
5284
5530
  assertShape(
5285
5531
  args,
5286
5532
  (yup3) => yup3.object({ params: yup3.object().required() })
5287
5533
  );
5288
- assertShape(
5289
- args?.params,
5290
- (yup3) => yup3.object({ relativePath: yup3.string().required() })
5291
- );
5292
- const doc = await this.getDocument(realPath);
5293
- const newRealPath = path3.join(
5294
- collection?.path,
5295
- args.params.relativePath
5296
- );
5297
- if (newRealPath === realPath) {
5298
- return doc;
5299
- }
5300
- await this.database.put(newRealPath, doc._rawData, collection.name);
5301
- await this.deleteDocument(realPath);
5302
- const collRefs = await this.findReferences(realPath, collection);
5303
- for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5304
- for (const [pathToDocWithRef, referencePaths] of Object.entries(
5305
- docsWithRefs
5306
- )) {
5307
- let docWithRef = await this.getRaw(pathToDocWithRef);
5308
- let hasUpdate = false;
5309
- for (const path7 of referencePaths) {
5310
- const { object: object2, updated } = updateObjectWithJsonPath(
5311
- docWithRef,
5312
- path7,
5313
- realPath,
5314
- newRealPath
5315
- );
5316
- docWithRef = object2;
5317
- hasUpdate = updated || hasUpdate;
5318
- }
5319
- if (hasUpdate) {
5320
- const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5321
- if (!collectionWithRef) {
5322
- throw new Error(
5323
- `Unable to find collection for ${pathToDocWithRef}`
5324
- );
5325
- }
5326
- await this.database.put(
5327
- pathToDocWithRef,
5328
- docWithRef,
5329
- collectionWithRef.name
5330
- );
5331
- }
5332
- }
5333
- }
5334
- return this.getDocument(newRealPath);
5335
- }
5336
- if (alreadyExists === false) {
5337
- throw new Error(
5338
- `Unable to update document, ${realPath} does not exist`
5339
- );
5534
+ return this.resolveUpdateDocument({
5535
+ collectionName: collection.name,
5536
+ relativePath: args.relativePath,
5537
+ newBody: isCollectionSpecific ? args.params : args.params[collection.name]
5538
+ });
5340
5539
  }
5341
- return this.updateResolveDocument({
5342
- collection,
5343
- realPath,
5344
- args,
5345
- isAddPendingDocument,
5346
- isCollectionSpecific
5347
- });
5348
5540
  } else {
5541
+ const realPath = path3.join(collection.path, args.relativePath);
5349
5542
  return this.getDocument(realPath, {
5350
5543
  collection,
5351
5544
  checkReferences: true
@@ -5385,7 +5578,7 @@ var Resolver = class {
5385
5578
  first: -1
5386
5579
  },
5387
5580
  collection: referencedCollection,
5388
- hydrator: (path7) => path7
5581
+ hydrator: (path8) => path8
5389
5582
  // just return the path
5390
5583
  }
5391
5584
  );
@@ -5700,6 +5893,7 @@ var resolveDateInput = (value) => {
5700
5893
  };
5701
5894
 
5702
5895
  // src/resolver/auth-fields.ts
5896
+ import path4 from "path";
5703
5897
  import { set } from "es-toolkit/compat";
5704
5898
  async function getUserDocumentContext(tinaSchema, resolver) {
5705
5899
  const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
@@ -5716,13 +5910,14 @@ async function getUserDocumentContext(tinaSchema, resolver) {
5716
5910
  );
5717
5911
  }
5718
5912
  const userField = userFields[0];
5719
- const realPath = `${collection.path}/index.json`;
5913
+ const relativePath = "index.json";
5914
+ const realPath = path4.join(collection.path, relativePath);
5720
5915
  const userDoc = await resolver.getDocument(realPath);
5721
5916
  const users = get(userDoc, userField.path);
5722
5917
  if (!users) {
5723
5918
  throw new Error("No users found");
5724
5919
  }
5725
- return { collection, userField, users, userDoc, realPath };
5920
+ return { collection, userField, users, userDoc, relativePath };
5726
5921
  }
5727
5922
  function findUserInCollection(users, userField, userSub) {
5728
5923
  const { idFieldName } = userField;
@@ -5784,7 +5979,7 @@ async function handleUpdatePassword({
5784
5979
  if (!password) {
5785
5980
  throw new Error("No password provided");
5786
5981
  }
5787
- const { collection, userField, users, realPath } = await getUserDocumentContext(tinaSchema, resolver);
5982
+ const { collection, userField, users, relativePath } = await getUserDocumentContext(tinaSchema, resolver);
5788
5983
  const { idFieldName, passwordFieldName } = userField;
5789
5984
  const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5790
5985
  if (!user) {
@@ -5794,9 +5989,9 @@ async function handleUpdatePassword({
5794
5989
  value: password,
5795
5990
  passwordChangeRequired: false
5796
5991
  };
5797
- const params = {};
5992
+ const newBody = {};
5798
5993
  set(
5799
- params,
5994
+ newBody,
5800
5995
  userField.path.slice(1),
5801
5996
  // remove _rawData from users path
5802
5997
  users.map((u) => {
@@ -5813,12 +6008,10 @@ async function handleUpdatePassword({
5813
6008
  };
5814
6009
  })
5815
6010
  );
5816
- await resolver.updateResolveDocument({
5817
- collection,
5818
- args: { params },
5819
- realPath,
5820
- isCollectionSpecific: true,
5821
- isAddPendingDocument: false
6011
+ await resolver.resolveUpdateDocument({
6012
+ collectionName: collection.name,
6013
+ relativePath,
6014
+ newBody
5822
6015
  });
5823
6016
  return true;
5824
6017
  }
@@ -5826,8 +6019,8 @@ async function handleUpdatePassword({
5826
6019
  // src/error.ts
5827
6020
  import { GraphQLError as GraphQLError3 } from "graphql";
5828
6021
  var NotFoundError = class extends GraphQLError3 {
5829
- constructor(message, nodes, source, positions, path7, originalError, extensions) {
5830
- super(message, nodes, source, positions, path7, originalError, extensions);
6022
+ constructor(message, nodes, source, positions, path8, originalError, extensions) {
6023
+ super(message, nodes, source, positions, path8, originalError, extensions);
5831
6024
  this.name = "NotFoundError";
5832
6025
  }
5833
6026
  };
@@ -5852,10 +6045,8 @@ var resolve = async ({
5852
6045
  const graphQLSchema = buildASTSchema(graphQLSchemaAst);
5853
6046
  const tinaConfig = await database.getTinaSchema();
5854
6047
  const tinaSchema = await createSchema({
5855
- // TODO: please update all the types to import from @tinacms/schema-tools
5856
- // @ts-ignore
5857
6048
  schema: tinaConfig,
5858
- // @ts-ignore
6049
+ // @ts-expect-error
5859
6050
  flags: tinaConfig?.meta?.flags
5860
6051
  });
5861
6052
  const resolver = createResolver({
@@ -5880,91 +6071,61 @@ var resolve = async ({
5880
6071
  }
5881
6072
  throw new Error(`Unable to find lookup key for ${namedType}`);
5882
6073
  },
5883
- fieldResolver: async (source = {}, _args = {}, _context, info) => {
6074
+ fieldResolver: async (source = {}, args = {}, _context, info) => {
5884
6075
  try {
5885
- const args = JSON.parse(JSON.stringify(_args));
5886
6076
  const returnType = getNamedType(info.returnType).toString();
5887
- const lookup = await database.getLookup(returnType);
5888
- const isMutation = info.parentType.toString() === "Mutation";
5889
- const value = source[info.fieldName];
5890
6077
  if (returnType === "Collection") {
5891
- if (value) {
5892
- return value;
6078
+ const possibleCollectionValue = source[info.fieldName];
6079
+ if (possibleCollectionValue) {
6080
+ return possibleCollectionValue;
5893
6081
  }
5894
6082
  if (info.fieldName === "collections") {
5895
- const collectionNode2 = info.fieldNodes.find(
5896
- (x) => x.name.value === "collections"
5897
- );
5898
- const hasDocuments2 = collectionNode2.selectionSet.selections.find(
5899
- (x) => {
5900
- return x?.name?.value === "documents";
5901
- }
5902
- );
5903
- return tinaSchema.getCollections().map((collection) => {
5904
- return resolver.resolveCollection(
5905
- args,
5906
- collection.name,
5907
- Boolean(hasDocuments2)
5908
- );
5909
- });
6083
+ return handleCollectionsField(info, tinaSchema, resolver, args);
5910
6084
  }
5911
- const collectionNode = info.fieldNodes.find(
5912
- (x) => x.name.value === "collection"
5913
- );
5914
- const hasDocuments = collectionNode.selectionSet.selections.find(
5915
- (x) => {
5916
- return x?.name?.value === "documents";
5917
- }
5918
- );
5919
- return resolver.resolveCollection(
5920
- args,
5921
- args.collection,
5922
- Boolean(hasDocuments)
5923
- );
6085
+ return handleCollectionField(info, args, resolver);
5924
6086
  }
5925
6087
  if (info.fieldName === "getOptimizedQuery") {
5926
- try {
5927
- return args.queryString;
5928
- } catch (e) {
5929
- throw new Error(
5930
- `Invalid query provided, Error message: ${e.message}`
5931
- );
5932
- }
6088
+ return args.queryString || "";
5933
6089
  }
5934
6090
  if (info.fieldName === "authenticate") {
6091
+ const authArgs = args;
5935
6092
  return handleAuthenticate({
5936
6093
  tinaSchema,
5937
6094
  resolver,
5938
- sub: args.sub,
5939
- password: args.password,
6095
+ sub: authArgs.sub,
6096
+ password: authArgs.password,
5940
6097
  info,
5941
6098
  ctxUser
5942
6099
  });
5943
6100
  }
5944
6101
  if (info.fieldName === "authorize") {
6102
+ const authArgs = args;
5945
6103
  return handleAuthorize({
5946
6104
  tinaSchema,
5947
6105
  resolver,
5948
- sub: args.sub,
6106
+ sub: authArgs.sub,
5949
6107
  info,
5950
6108
  ctxUser
5951
6109
  });
5952
6110
  }
5953
6111
  if (info.fieldName === "updatePassword") {
6112
+ const authArgs = args;
5954
6113
  return handleUpdatePassword({
5955
6114
  tinaSchema,
5956
6115
  resolver,
5957
- password: args.password,
6116
+ password: authArgs.password,
5958
6117
  info,
5959
6118
  ctxUser
5960
6119
  });
5961
6120
  }
6121
+ const lookup = await database.getLookup(returnType);
5962
6122
  if (!lookup) {
5963
- return value;
6123
+ return source[info.fieldName];
5964
6124
  }
5965
6125
  const isCreation = lookup[info.fieldName] === "create";
6126
+ const isMutation = info.parentType.toString() === "Mutation";
5966
6127
  switch (lookup.resolveType) {
5967
- /**
6128
+ /*
5968
6129
  * `node(id: $id)`
5969
6130
  */
5970
6131
  case "nodeDocument":
@@ -5974,78 +6135,114 @@ var resolve = async ({
5974
6135
  );
5975
6136
  return resolver.getDocument(args.id);
5976
6137
  case "multiCollectionDocument":
5977
- if (typeof value === "string" && value !== "") {
5978
- return resolver.getDocument(value);
5979
- }
5980
- if (args?.collection && info.fieldName === "addPendingDocument") {
5981
- return resolver.resolveDocument({
5982
- args: { ...args, params: {} },
5983
- collection: args.collection,
5984
- isMutation,
5985
- isCreation: true,
5986
- isAddPendingDocument: true
5987
- });
6138
+ const possibleValue = source[info.fieldName];
6139
+ if (typeof possibleValue === "string" && possibleValue !== "") {
6140
+ return resolver.getDocument(possibleValue);
5988
6141
  }
5989
6142
  if ([
5990
6143
  NAMER.documentQueryName(),
6144
+ "addPendingDocument",
5991
6145
  "createDocument",
5992
6146
  "updateDocument",
5993
6147
  "deleteDocument",
5994
6148
  "createFolder"
5995
6149
  ].includes(info.fieldName)) {
5996
- const result = await resolver.resolveDocument({
6150
+ assertShape(
5997
6151
  args,
5998
- collection: args.collection,
5999
- isMutation,
6000
- isCreation,
6001
- // Right now this is the only case for deletion
6002
- isDeletion: info.fieldName === "deleteDocument",
6003
- isFolderCreation: info.fieldName === "createFolder",
6004
- isUpdateName: Boolean(args?.params?.relativePath),
6005
- isAddPendingDocument: false,
6006
- isCollectionSpecific: false
6007
- });
6008
- return result;
6152
+ (yup3) => yup3.object({
6153
+ collection: yup3.string().required(),
6154
+ relativePath: yup3.string().required()
6155
+ })
6156
+ );
6157
+ if (isMutation) {
6158
+ switch (info.fieldName) {
6159
+ case "addPendingDocument":
6160
+ return resolver.resolveAddPendingDocument({
6161
+ collectionName: args.collection,
6162
+ relativePath: args.relativePath,
6163
+ templateName: args.template
6164
+ });
6165
+ case "createFolder":
6166
+ return resolver.resolveCreateFolder({
6167
+ collectionName: args.collection,
6168
+ relativePath: args.relativePath
6169
+ });
6170
+ case "createDocument": {
6171
+ assertShape(
6172
+ args,
6173
+ (yup3) => yup3.object({
6174
+ params: yup3.object().shape({
6175
+ [args.collection]: yup3.object().required()
6176
+ }).required()
6177
+ })
6178
+ );
6179
+ return resolver.resolveCreateDocument({
6180
+ collectionName: args.collection,
6181
+ relativePath: args.relativePath,
6182
+ body: args.params[args.collection]
6183
+ });
6184
+ }
6185
+ case "updateDocument": {
6186
+ assertShape(
6187
+ args,
6188
+ (yup3) => yup3.object({
6189
+ params: yup3.object().shape({
6190
+ relativePath: yup3.string().optional()
6191
+ }).required()
6192
+ })
6193
+ );
6194
+ const newRelativePath = args.params.relativePath;
6195
+ const newBody = args.params[args.collection];
6196
+ return resolver.resolveUpdateDocument({
6197
+ collectionName: args.collection,
6198
+ relativePath: args.relativePath,
6199
+ newRelativePath,
6200
+ newBody
6201
+ });
6202
+ }
6203
+ case "deleteDocument":
6204
+ return resolver.resolveDeleteDocument({
6205
+ collectionName: args.collection,
6206
+ relativePath: args.relativePath
6207
+ });
6208
+ }
6209
+ } else if (info.fieldName === NAMER.documentQueryName()) {
6210
+ return resolver.resolveRetrievedDocument({
6211
+ collectionName: args.collection,
6212
+ relativePath: args.relativePath
6213
+ });
6214
+ }
6009
6215
  }
6010
- return value;
6011
- /**
6216
+ return possibleValue;
6217
+ /*
6012
6218
  * eg `getMovieDocument.data.actors`
6013
6219
  */
6014
6220
  case "multiCollectionDocumentList":
6015
- if (Array.isArray(value)) {
6221
+ const listValue = source[info.fieldName];
6222
+ if (Array.isArray(listValue)) {
6016
6223
  return {
6017
- totalCount: value.length,
6018
- edges: value.map((document) => {
6224
+ totalCount: listValue.length,
6225
+ edges: listValue.map((document) => {
6019
6226
  return { node: document };
6020
6227
  })
6021
6228
  };
6022
6229
  }
6023
- if (info.fieldName === "documents" && value?.collection && value?.hasDocuments) {
6024
- let filter = args.filter;
6025
- if (
6026
- // 1. Make sure that the filter exists
6027
- typeof args?.filter !== "undefined" && args?.filter !== null && // 2. Make sure that the collection name exists
6028
- // @ts-ignore
6029
- typeof value?.collection?.name === "string" && // 3. Make sure that the collection name is in the filter and is not undefined
6030
- // @ts-ignore
6031
- Object.keys(args.filter).includes(value?.collection?.name) && // @ts-ignore
6032
- typeof args.filter[value?.collection?.name] !== "undefined"
6033
- ) {
6034
- filter = args.filter[value.collection.name];
6035
- }
6230
+ if (info.fieldName === "documents" && listValue?.collection && listValue?.hasDocuments) {
6231
+ const documentsArgs = args;
6232
+ const collectionName = listValue.collection?.name;
6233
+ const filter = (collectionName && documentsArgs.filter?.[collectionName]) ?? documentsArgs.filter;
6036
6234
  return resolver.resolveCollectionConnection({
6037
6235
  args: {
6038
- ...args,
6236
+ ...documentsArgs,
6039
6237
  filter
6040
6238
  },
6041
- // @ts-ignore
6042
- collection: value.collection
6239
+ collection: listValue.collection
6043
6240
  });
6044
6241
  }
6045
6242
  throw new Error(
6046
6243
  `Expected an array for result of ${info.fieldName} at ${info.path}`
6047
6244
  );
6048
- /**
6245
+ /*
6049
6246
  * Collections-specific getter
6050
6247
  * eg. `getPostDocument`/`createPostDocument`/`updatePostDocument`
6051
6248
  *
@@ -6053,29 +6250,54 @@ var resolve = async ({
6053
6250
  * the field will be `node`
6054
6251
  */
6055
6252
  case "collectionDocument": {
6056
- if (value) {
6057
- return value;
6253
+ const possibleDocValue = source[info.fieldName];
6254
+ if (possibleDocValue) {
6255
+ return possibleDocValue;
6058
6256
  }
6059
- const result = value || await resolver.resolveDocument({
6257
+ assertShape(
6060
6258
  args,
6061
- collection: lookup.collection,
6062
- isMutation,
6063
- isCreation,
6064
- isAddPendingDocument: false,
6065
- isCollectionSpecific: true
6066
- });
6067
- return result;
6259
+ (yup3) => yup3.object({
6260
+ relativePath: yup3.string().required()
6261
+ })
6262
+ );
6263
+ if (isMutation) {
6264
+ assertShape(
6265
+ args,
6266
+ (yup3) => yup3.object({
6267
+ params: yup3.object().required()
6268
+ })
6269
+ );
6270
+ if (isCreation) {
6271
+ return resolver.resolveCreateDocument({
6272
+ collectionName: lookup.collection,
6273
+ relativePath: args.relativePath,
6274
+ body: args.params
6275
+ });
6276
+ } else {
6277
+ return resolver.resolveUpdateDocument({
6278
+ collectionName: lookup.collection,
6279
+ relativePath: args.relativePath,
6280
+ newBody: args.params
6281
+ });
6282
+ }
6283
+ } else {
6284
+ return resolver.resolveRetrievedDocument({
6285
+ collectionName: lookup.collection,
6286
+ relativePath: args.relativePath
6287
+ });
6288
+ }
6068
6289
  }
6069
- /**
6290
+ /*
6070
6291
  * Collections-specific list getter
6071
6292
  * eg. `getPageList`
6072
6293
  */
6073
6294
  case "collectionDocumentList":
6295
+ const collectionArgs = args;
6074
6296
  return resolver.resolveCollectionConnection({
6075
- args,
6297
+ args: collectionArgs,
6076
6298
  collection: tinaSchema.getCollection(lookup.collection)
6077
6299
  });
6078
- /**
6300
+ /*
6079
6301
  * A polymorphic data set, it can be from a document's data
6080
6302
  * of any nested object which can be one of many shapes
6081
6303
  *
@@ -6093,22 +6315,52 @@ var resolve = async ({
6093
6315
  * ```
6094
6316
  */
6095
6317
  case "unionData":
6096
- if (!value) {
6097
- if (args.relativePath) {
6098
- const result = await resolver.resolveDocument({
6318
+ const unionValue = source[info.fieldName];
6319
+ if (!unionValue) {
6320
+ const unionArgs = args;
6321
+ if (unionArgs.relativePath) {
6322
+ assertShape(
6099
6323
  args,
6100
- collection: lookup.collection,
6101
- isMutation,
6102
- isCreation,
6103
- isAddPendingDocument: false,
6104
- isCollectionSpecific: true
6105
- });
6106
- return result;
6324
+ (yup3) => yup3.object({
6325
+ relativePath: yup3.string().required()
6326
+ })
6327
+ );
6328
+ if (isMutation) {
6329
+ assertShape(
6330
+ args,
6331
+ (yup3) => yup3.object({
6332
+ params: yup3.object().required()
6333
+ })
6334
+ );
6335
+ if (isCreation) {
6336
+ return resolver.resolveCreateDocument({
6337
+ collectionName: lookup.collection,
6338
+ relativePath: args.relativePath,
6339
+ body: args.params
6340
+ });
6341
+ } else {
6342
+ return resolver.resolveUpdateDocument({
6343
+ collectionName: lookup.collection,
6344
+ relativePath: args.relativePath,
6345
+ newBody: args.params
6346
+ });
6347
+ }
6348
+ } else {
6349
+ return resolver.resolveRetrievedDocument({
6350
+ collectionName: lookup.collection,
6351
+ relativePath: args.relativePath
6352
+ });
6353
+ }
6107
6354
  }
6108
6355
  }
6109
- return value;
6356
+ return unionValue;
6110
6357
  default:
6111
- console.error(lookup);
6358
+ console.error(
6359
+ `Could not recognize resolve type '${lookup.resolveType}'.`
6360
+ );
6361
+ console.error(
6362
+ "The field resolver needs to be updated to handle this new type."
6363
+ );
6112
6364
  throw new Error("Unexpected resolve type");
6113
6365
  }
6114
6366
  } catch (e) {
@@ -6144,6 +6396,40 @@ var resolve = async ({
6144
6396
  throw e;
6145
6397
  }
6146
6398
  };
6399
+ var handleCollectionsField = (info, tinaSchema, resolver, args) => {
6400
+ const collectionNode = info.fieldNodes.find(
6401
+ (x) => x.name.value === "collections"
6402
+ );
6403
+ const hasDocuments = collectionNode.selectionSet.selections.find(
6404
+ (x) => x.kind == "Field" && x?.name?.value === "documents"
6405
+ );
6406
+ return tinaSchema.getCollections().map((collection) => {
6407
+ return resolver.resolveCollection(
6408
+ args,
6409
+ collection.name,
6410
+ Boolean(hasDocuments)
6411
+ );
6412
+ });
6413
+ };
6414
+ var handleCollectionField = (info, args, resolver) => {
6415
+ const collectionNode = info.fieldNodes.find(
6416
+ (x) => x.name.value === "collection"
6417
+ );
6418
+ const hasDocuments = collectionNode.selectionSet.selections.find(
6419
+ (x) => x.kind == "Field" && x?.name?.value === "documents"
6420
+ );
6421
+ assertShape(
6422
+ args,
6423
+ (yup3) => yup3.object({
6424
+ collection: yup3.string().required()
6425
+ })
6426
+ );
6427
+ return resolver.resolveCollection(
6428
+ args,
6429
+ args.collection,
6430
+ Boolean(hasDocuments)
6431
+ );
6432
+ };
6147
6433
 
6148
6434
  // src/level/tinaLevel.ts
6149
6435
  import { ManyLevelGuest } from "many-level";
@@ -6167,7 +6453,7 @@ var TinaLevelClient = class extends ManyLevelGuest {
6167
6453
  };
6168
6454
 
6169
6455
  // src/database/index.ts
6170
- import path4 from "node:path";
6456
+ import path5 from "node:path";
6171
6457
  import { GraphQLError as GraphQLError5 } from "graphql";
6172
6458
  import micromatch2 from "micromatch";
6173
6459
  import sha2 from "js-sha1";
@@ -6258,7 +6544,7 @@ var Database = class {
6258
6544
  } catch (e) {
6259
6545
  }
6260
6546
  };
6261
- getGeneratedFolder = () => path4.join(this.tinaDirectory, "__generated__");
6547
+ getGeneratedFolder = () => path5.join(this.tinaDirectory, "__generated__");
6262
6548
  async updateDatabaseVersion(version) {
6263
6549
  let metadataLevel = this.rootLevel.sublevel("_metadata", SUBLEVEL_OPTIONS);
6264
6550
  if (this.contentNamespace) {
@@ -6674,7 +6960,7 @@ var Database = class {
6674
6960
  );
6675
6961
  const writeTemplateKey = templateDetails.info.type === "union";
6676
6962
  const aliasedData = applyNameOverrides(templateDetails.template, payload);
6677
- const extension = path4.extname(filepath);
6963
+ const extension = path5.extname(filepath);
6678
6964
  return stringifyFile(
6679
6965
  aliasedData,
6680
6966
  extension,
@@ -6706,7 +6992,7 @@ var Database = class {
6706
6992
  getLookup = async (returnType) => {
6707
6993
  await this.initLevel();
6708
6994
  const lookupPath = normalizePath(
6709
- path4.join(this.getGeneratedFolder(), `_lookup.json`)
6995
+ path5.join(this.getGeneratedFolder(), `_lookup.json`)
6710
6996
  );
6711
6997
  if (!this._lookup) {
6712
6998
  this._lookup = await this.contentLevel.sublevel(
@@ -6719,7 +7005,7 @@ var Database = class {
6719
7005
  getGraphQLSchema = async () => {
6720
7006
  await this.initLevel();
6721
7007
  const graphqlPath = normalizePath(
6722
- path4.join(this.getGeneratedFolder(), `_graphql.json`)
7008
+ path5.join(this.getGeneratedFolder(), `_graphql.json`)
6723
7009
  );
6724
7010
  return await this.contentLevel.sublevel(
6725
7011
  CONTENT_ROOT_PREFIX,
@@ -6732,7 +7018,7 @@ var Database = class {
6732
7018
  throw new Error(`No bridge configured`);
6733
7019
  }
6734
7020
  const graphqlPath = normalizePath(
6735
- path4.join(this.getGeneratedFolder(), `_graphql.json`)
7021
+ path5.join(this.getGeneratedFolder(), `_graphql.json`)
6736
7022
  );
6737
7023
  const _graphql = await this.bridge.get(graphqlPath);
6738
7024
  return JSON.parse(_graphql);
@@ -6740,7 +7026,7 @@ var Database = class {
6740
7026
  getTinaSchema = async (level) => {
6741
7027
  await this.initLevel();
6742
7028
  const schemaPath = normalizePath(
6743
- path4.join(this.getGeneratedFolder(), `_schema.json`)
7029
+ path5.join(this.getGeneratedFolder(), `_schema.json`)
6744
7030
  );
6745
7031
  return await (level || this.contentLevel).sublevel(
6746
7032
  CONTENT_ROOT_PREFIX,
@@ -6756,7 +7042,7 @@ var Database = class {
6756
7042
  if (!schema) {
6757
7043
  throw new Error(
6758
7044
  `Unable to get schema from level db: ${normalizePath(
6759
- path4.join(this.getGeneratedFolder(), `_schema.json`)
7045
+ path5.join(this.getGeneratedFolder(), `_schema.json`)
6760
7046
  )}`
6761
7047
  );
6762
7048
  }
@@ -6989,21 +7275,21 @@ var Database = class {
6989
7275
  edges,
6990
7276
  async ({
6991
7277
  cursor,
6992
- path: path7,
7278
+ path: path8,
6993
7279
  value
6994
7280
  }) => {
6995
7281
  try {
6996
- const node = await hydrator(path7, value);
7282
+ const node = await hydrator(path8, value);
6997
7283
  return {
6998
7284
  node,
6999
7285
  cursor: btoa(cursor)
7000
7286
  };
7001
7287
  } catch (error) {
7002
7288
  console.log(error);
7003
- if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
7289
+ if (error instanceof Error && (!path8.includes(".tina/__generated__/_graphql.json") || !path8.includes("tina/__generated__/_graphql.json"))) {
7004
7290
  throw new TinaQueryError({
7005
7291
  originalError: error,
7006
- file: path7,
7292
+ file: path8,
7007
7293
  collection: collection.name
7008
7294
  });
7009
7295
  }
@@ -7050,7 +7336,7 @@ var Database = class {
7050
7336
  lookup = lookupFromLockFile || JSON.parse(
7051
7337
  await this.bridge.get(
7052
7338
  normalizePath(
7053
- path4.join(this.getGeneratedFolder(), "_lookup.json")
7339
+ path5.join(this.getGeneratedFolder(), "_lookup.json")
7054
7340
  )
7055
7341
  )
7056
7342
  );
@@ -7074,15 +7360,15 @@ var Database = class {
7074
7360
  }
7075
7361
  const contentRootLevel = nextLevel.sublevel(CONTENT_ROOT_PREFIX, SUBLEVEL_OPTIONS);
7076
7362
  await contentRootLevel.put(
7077
- normalizePath(path4.join(this.getGeneratedFolder(), "_graphql.json")),
7363
+ normalizePath(path5.join(this.getGeneratedFolder(), "_graphql.json")),
7078
7364
  graphQLSchema
7079
7365
  );
7080
7366
  await contentRootLevel.put(
7081
- normalizePath(path4.join(this.getGeneratedFolder(), "_schema.json")),
7367
+ normalizePath(path5.join(this.getGeneratedFolder(), "_schema.json")),
7082
7368
  tinaSchema.schema
7083
7369
  );
7084
7370
  await contentRootLevel.put(
7085
- normalizePath(path4.join(this.getGeneratedFolder(), "_lookup.json")),
7371
+ normalizePath(path5.join(this.getGeneratedFolder(), "_lookup.json")),
7086
7372
  lookup
7087
7373
  );
7088
7374
  const result = await this._indexAllContent(
@@ -7291,8 +7577,8 @@ var Database = class {
7291
7577
  return { warnings };
7292
7578
  };
7293
7579
  };
7294
- var hashPasswordVisitor = async (node, path7) => {
7295
- const passwordValuePath = [...path7, "value"];
7580
+ var hashPasswordVisitor = async (node, path8) => {
7581
+ const passwordValuePath = [...path8, "value"];
7296
7582
  const plaintextPassword = get(node, passwordValuePath);
7297
7583
  if (plaintextPassword) {
7298
7584
  set2(
@@ -7302,10 +7588,10 @@ var hashPasswordVisitor = async (node, path7) => {
7302
7588
  );
7303
7589
  }
7304
7590
  };
7305
- var visitNodes = async (node, path7, callback) => {
7306
- const [currentLevel, ...remainingLevels] = path7;
7591
+ var visitNodes = async (node, path8, callback) => {
7592
+ const [currentLevel, ...remainingLevels] = path8;
7307
7593
  if (!remainingLevels?.length) {
7308
- return callback(node, path7);
7594
+ return callback(node, path8);
7309
7595
  }
7310
7596
  if (Array.isArray(node[currentLevel])) {
7311
7597
  for (const item of node[currentLevel]) {
@@ -7548,13 +7834,13 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7548
7834
  // src/git/index.ts
7549
7835
  import git from "isomorphic-git";
7550
7836
  import fs from "fs-extra";
7551
- import path5 from "path";
7837
+ import path6 from "path";
7552
7838
  import micromatch3 from "micromatch";
7553
7839
  var findGitRoot = async (dir) => {
7554
- if (await fs.pathExists(path5.join(dir, ".git"))) {
7840
+ if (await fs.pathExists(path6.join(dir, ".git"))) {
7555
7841
  return dir;
7556
7842
  }
7557
- const parentDir = path5.dirname(dir);
7843
+ const parentDir = path6.dirname(dir);
7558
7844
  if (parentDir === dir) {
7559
7845
  throw new Error("Could not find .git directory");
7560
7846
  }
@@ -7636,19 +7922,19 @@ var shaExists = async ({
7636
7922
  // src/database/bridge/filesystem.ts
7637
7923
  import fs2 from "fs-extra";
7638
7924
  import fg from "fast-glob";
7639
- import path6 from "path";
7925
+ import path7 from "path";
7640
7926
  import normalize from "normalize-path";
7641
7927
  var FilesystemBridge = class {
7642
7928
  rootPath;
7643
7929
  outputPath;
7644
7930
  constructor(rootPath, outputPath) {
7645
- this.rootPath = path6.resolve(rootPath);
7646
- this.outputPath = outputPath ? path6.resolve(outputPath) : this.rootPath;
7931
+ this.rootPath = path7.resolve(rootPath);
7932
+ this.outputPath = outputPath ? path7.resolve(outputPath) : this.rootPath;
7647
7933
  }
7648
7934
  async glob(pattern, extension) {
7649
- const basePath = path6.join(this.outputPath, ...pattern.split("/"));
7935
+ const basePath = path7.join(this.outputPath, ...pattern.split("/"));
7650
7936
  const items = await fg(
7651
- path6.join(basePath, "**", `/*.${extension}`).replace(/\\/g, "/"),
7937
+ path7.join(basePath, "**", `/*.${extension}`).replace(/\\/g, "/"),
7652
7938
  {
7653
7939
  dot: true,
7654
7940
  ignore: ["**/node_modules/**"]
@@ -7660,14 +7946,14 @@ var FilesystemBridge = class {
7660
7946
  );
7661
7947
  }
7662
7948
  async delete(filepath) {
7663
- await fs2.remove(path6.join(this.outputPath, filepath));
7949
+ await fs2.remove(path7.join(this.outputPath, filepath));
7664
7950
  }
7665
7951
  async get(filepath) {
7666
- return (await fs2.readFile(path6.join(this.outputPath, filepath))).toString();
7952
+ return (await fs2.readFile(path7.join(this.outputPath, filepath))).toString();
7667
7953
  }
7668
7954
  async put(filepath, data, basePathOverride) {
7669
7955
  const basePath = basePathOverride || this.outputPath;
7670
- await fs2.outputFile(path6.join(basePath, filepath), data);
7956
+ await fs2.outputFile(path7.join(basePath, filepath), data);
7671
7957
  }
7672
7958
  };
7673
7959
  var AuditFileSystemBridge = class extends FilesystemBridge {
@@ -7771,7 +8057,7 @@ var IsomorphicBridge = class {
7771
8057
  async listEntries({
7772
8058
  pattern,
7773
8059
  entry,
7774
- path: path7,
8060
+ path: path8,
7775
8061
  results
7776
8062
  }) {
7777
8063
  const treeResult = await git2.readTree({
@@ -7781,7 +8067,7 @@ var IsomorphicBridge = class {
7781
8067
  });
7782
8068
  const children = [];
7783
8069
  for (const childEntry of treeResult.tree) {
7784
- const childPath = path7 ? `${path7}/${childEntry.path}` : childEntry.path;
8070
+ const childPath = path8 ? `${path8}/${childEntry.path}` : childEntry.path;
7785
8071
  if (childEntry.type === "tree") {
7786
8072
  children.push(childEntry);
7787
8073
  } else {
@@ -7791,7 +8077,7 @@ var IsomorphicBridge = class {
7791
8077
  }
7792
8078
  }
7793
8079
  for (const childEntry of children) {
7794
- const childPath = path7 ? `${path7}/${childEntry.path}` : childEntry.path;
8080
+ const childPath = path8 ? `${path8}/${childEntry.path}` : childEntry.path;
7795
8081
  await this.listEntries({
7796
8082
  pattern,
7797
8083
  entry: childEntry,
@@ -7809,17 +8095,17 @@ var IsomorphicBridge = class {
7809
8095
  * @param ref - ref to resolve path entries for
7810
8096
  * @private
7811
8097
  */
7812
- async resolvePathEntries(path7, ref) {
7813
- let pathParts = path7.split("/");
8098
+ async resolvePathEntries(path8, ref) {
8099
+ let pathParts = path8.split("/");
7814
8100
  const result = await git2.walk({
7815
8101
  ...this.isomorphicConfig,
7816
8102
  map: async (filepath, [head]) => {
7817
8103
  if (head._fullpath === ".") {
7818
8104
  return head;
7819
8105
  }
7820
- if (path7.startsWith(filepath)) {
7821
- if (dirname(path7) === dirname(filepath)) {
7822
- if (path7 === filepath) {
8106
+ if (path8.startsWith(filepath)) {
8107
+ if (dirname(path8) === dirname(filepath)) {
8108
+ if (path8 === filepath) {
7823
8109
  return head;
7824
8110
  }
7825
8111
  } else {
@@ -7850,7 +8136,7 @@ var IsomorphicBridge = class {
7850
8136
  * @param pathParts - parent path parts
7851
8137
  * @private
7852
8138
  */
7853
- async updateTreeHierarchy(existingOid, updatedOid, path7, type, pathEntries, pathParts) {
8139
+ async updateTreeHierarchy(existingOid, updatedOid, path8, type, pathEntries, pathParts) {
7854
8140
  const lastIdx = pathEntries.length - 1;
7855
8141
  const parentEntry = pathEntries[lastIdx];
7856
8142
  const parentPath = pathParts[lastIdx];
@@ -7865,7 +8151,7 @@ var IsomorphicBridge = class {
7865
8151
  cache: this.cache
7866
8152
  });
7867
8153
  tree = existingOid ? treeResult.tree.map((entry) => {
7868
- if (entry.path === path7) {
8154
+ if (entry.path === path8) {
7869
8155
  entry.oid = updatedOid;
7870
8156
  }
7871
8157
  return entry;
@@ -7874,7 +8160,7 @@ var IsomorphicBridge = class {
7874
8160
  {
7875
8161
  oid: updatedOid,
7876
8162
  type,
7877
- path: path7,
8163
+ path: path8,
7878
8164
  mode
7879
8165
  }
7880
8166
  ];
@@ -7883,7 +8169,7 @@ var IsomorphicBridge = class {
7883
8169
  {
7884
8170
  oid: updatedOid,
7885
8171
  type,
7886
- path: path7,
8172
+ path: path8,
7887
8173
  mode
7888
8174
  }
7889
8175
  ];
@@ -7992,7 +8278,7 @@ var IsomorphicBridge = class {
7992
8278
  path: parentPath,
7993
8279
  results
7994
8280
  });
7995
- return results.map((path7) => this.unqualifyPath(path7)).filter((path7) => path7.endsWith(extension));
8281
+ return results.map((path8) => this.unqualifyPath(path8)).filter((path8) => path8.endsWith(extension));
7996
8282
  }
7997
8283
  async delete(filepath) {
7998
8284
  const ref = await this.getRef();