@tinacms/graphql 0.0.0-b832ee4-20250102012112 → 0.0.0-bb1ad6a-20251201062640

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  // src/build.ts
2
- import { print } from "graphql";
3
- import uniqBy2 from "lodash.uniqby";
2
+ import {
3
+ print
4
+ } from "graphql";
5
+ import { uniqBy as uniqBy2 } from "es-toolkit";
4
6
 
5
7
  // src/util.ts
6
8
  import * as yup from "yup";
@@ -56,7 +58,7 @@ var flattenDeep = (arr) => arr.flatMap(
56
58
  );
57
59
 
58
60
  // src/ast-builder/index.ts
59
- import uniqBy from "lodash.uniqby";
61
+ import { uniqBy } from "es-toolkit";
60
62
  var SysFieldDefinition = {
61
63
  kind: "Field",
62
64
  name: {
@@ -977,6 +979,7 @@ var astBuilder = {
977
979
  ...extractInlineTypes(ast.globalTemplates),
978
980
  ...ast.definitions
979
981
  ],
982
+ // @ts-ignore - all nodes have a name property in practice
980
983
  (field) => field.name.value
981
984
  );
982
985
  return {
@@ -1029,41 +1032,6 @@ function* walk(maybeNode, visited = /* @__PURE__ */ new WeakSet()) {
1029
1032
  yield maybeNode;
1030
1033
  visited.add(maybeNode);
1031
1034
  }
1032
- function addNamespaceToSchema(maybeNode, namespace = []) {
1033
- if (typeof maybeNode === "string") {
1034
- return maybeNode;
1035
- }
1036
- if (typeof maybeNode === "boolean") {
1037
- return maybeNode;
1038
- }
1039
- const newNode = maybeNode;
1040
- const keys = Object.keys(maybeNode);
1041
- Object.values(maybeNode).map((m, index) => {
1042
- const key = keys[index];
1043
- if (Array.isArray(m)) {
1044
- newNode[key] = m.map((element) => {
1045
- if (!element) {
1046
- return;
1047
- }
1048
- if (!element.hasOwnProperty("name")) {
1049
- return element;
1050
- }
1051
- const value = element.name || element.value;
1052
- return addNamespaceToSchema(element, [...namespace, value]);
1053
- });
1054
- } else {
1055
- if (!m) {
1056
- return;
1057
- }
1058
- if (!m.hasOwnProperty("name")) {
1059
- newNode[key] = m;
1060
- } else {
1061
- newNode[key] = addNamespaceToSchema(m, [...namespace, m.name]);
1062
- }
1063
- }
1064
- });
1065
- return { ...newNode, namespace };
1066
- }
1067
1035
  var generateNamespacedFieldName = (names, suffix = "") => {
1068
1036
  return (suffix ? [...names, suffix] : names).map(capitalize).join("");
1069
1037
  };
@@ -1882,7 +1850,7 @@ var Builder = class {
1882
1850
  * ```
1883
1851
  *
1884
1852
  * @public
1885
- * @param collection a Tina Cloud collection
1853
+ * @param collection a TinaCloud collection
1886
1854
  */
1887
1855
  this.collectionFragment = async (collection) => {
1888
1856
  const name = NAMER.dataTypeName(collection.namespace);
@@ -2746,7 +2714,7 @@ var Builder = class {
2746
2714
  this._buildDataField = async (field) => {
2747
2715
  const listWarningMsg = `
2748
2716
  WARNING: The user interface for ${field.type} does not support \`list: true\`
2749
- Visit https://tina.io/docs/errors/ui-not-supported/ for more information
2717
+ Visit https://tina.io/docs/r/content-fields/#list-fields/ for more information
2750
2718
 
2751
2719
  `;
2752
2720
  switch (field.type) {
@@ -2898,8 +2866,9 @@ var filterSelections = (arr) => {
2898
2866
  import { TinaSchema } from "@tinacms/schema-tools";
2899
2867
 
2900
2868
  // src/schema/validate.ts
2901
- import deepClone from "lodash.clonedeep";
2869
+ import { addNamespaceToSchema } from "@tinacms/schema-tools";
2902
2870
  import * as yup2 from "yup";
2871
+ import { cloneDeep } from "es-toolkit";
2903
2872
  import {
2904
2873
  validateTinaCloudSchemaConfig
2905
2874
  } from "@tinacms/schema-tools";
@@ -2916,7 +2885,7 @@ var FIELD_TYPES = [
2916
2885
  ];
2917
2886
  var validateSchema = async (schema) => {
2918
2887
  const schema2 = addNamespaceToSchema(
2919
- deepClone(schema)
2888
+ cloneDeep(schema)
2920
2889
  );
2921
2890
  const collections = await sequential(
2922
2891
  schema2.collections,
@@ -3053,7 +3022,7 @@ var validateField = async (field) => {
3053
3022
  // package.json
3054
3023
  var package_default = {
3055
3024
  name: "@tinacms/graphql",
3056
- version: "1.5.9",
3025
+ version: "1.6.3",
3057
3026
  main: "dist/index.js",
3058
3027
  module: "dist/index.mjs",
3059
3028
  typings: "dist/index.d.ts",
@@ -3079,34 +3048,31 @@ var package_default = {
3079
3048
  types: "pnpm tsc",
3080
3049
  build: "tinacms-scripts build",
3081
3050
  docs: "pnpm typedoc",
3082
- serve: "pnpm nodemon dist/server.js",
3083
- test: "jest",
3084
- "test-watch": "jest --watch"
3051
+ test: "vitest run",
3052
+ "test-watch": "vitest"
3085
3053
  },
3086
3054
  dependencies: {
3087
- "@iarna/toml": "^2.2.5",
3055
+ "@iarna/toml": "catalog:",
3088
3056
  "@tinacms/mdx": "workspace:*",
3089
3057
  "@tinacms/schema-tools": "workspace:*",
3090
- "abstract-level": "^1.0.4",
3058
+ "abstract-level": "catalog:",
3091
3059
  "date-fns": "^2.30.0",
3092
- "fast-glob": "^3.3.2",
3093
- "fs-extra": "^11.2.0",
3094
- "glob-parent": "^6.0.2",
3060
+ "es-toolkit": "^1.42.0",
3061
+ "fast-glob": "catalog:",
3062
+ "fs-extra": "catalog:",
3063
+ "glob-parent": "catalog:",
3095
3064
  graphql: "15.8.0",
3096
- "gray-matter": "^4.0.3",
3097
- "isomorphic-git": "^1.27.1",
3098
- "js-sha1": "^0.6.0",
3065
+ "gray-matter": "catalog:",
3066
+ "isomorphic-git": "catalog:",
3067
+ "js-sha1": "catalog:",
3099
3068
  "js-yaml": "^3.14.1",
3100
- "jsonpath-plus": "10.1.0",
3101
- "lodash.clonedeep": "^4.5.0",
3102
- "lodash.set": "^4.3.2",
3103
- "lodash.uniqby": "^4.7.0",
3104
- "many-level": "^2.0.0",
3105
- micromatch: "4.0.8",
3106
- "normalize-path": "^3.0.0",
3107
- "readable-stream": "^4.5.2",
3108
- scmp: "^2.1.0",
3109
- yup: "^0.32.11"
3069
+ "jsonpath-plus": "catalog:",
3070
+ "many-level": "catalog:",
3071
+ micromatch: "catalog:",
3072
+ "normalize-path": "catalog:",
3073
+ "readable-stream": "catalog:",
3074
+ scmp: "catalog:",
3075
+ yup: "^1.6.1"
3110
3076
  },
3111
3077
  publishConfig: {
3112
3078
  registry: "https://registry.npmjs.org"
@@ -3120,26 +3086,21 @@ var package_default = {
3120
3086
  "@tinacms/scripts": "workspace:*",
3121
3087
  "@types/cors": "^2.8.17",
3122
3088
  "@types/estree": "^0.0.50",
3123
- "@types/express": "^4.17.21",
3089
+ "@types/express": "catalog:",
3124
3090
  "@types/fs-extra": "^9.0.13",
3125
- "@types/jest": "^26.0.24",
3126
3091
  "@types/js-yaml": "^3.12.10",
3127
- "@types/lodash.camelcase": "^4.3.9",
3128
- "@types/lodash.upperfirst": "^4.3.9",
3129
- "@types/lru-cache": "^5.1.1",
3130
- "@types/mdast": "^3.0.15",
3131
- "@types/micromatch": "^4.0.9",
3132
- "@types/node": "^22.9.0",
3133
- "@types/normalize-path": "^3.0.2",
3134
- "@types/ws": "^7.4.7",
3135
- "@types/yup": "^0.29.14",
3136
- jest: "^29.7.0",
3137
- "jest-diff": "^29.7.0",
3092
+ "@types/lru-cache": "catalog:",
3093
+ "@types/mdast": "catalog:",
3094
+ "@types/micromatch": "catalog:",
3095
+ "@types/node": "^22.13.1",
3096
+ "@types/normalize-path": "catalog:",
3097
+ "@types/ws": "catalog:",
3138
3098
  "jest-file-snapshot": "^0.5.0",
3139
- "jest-matcher-utils": "^29.7.0",
3140
- "memory-level": "^1.0.0",
3141
- nodemon: "3.1.4",
3142
- typescript: "^5.6.3"
3099
+ "memory-level": "catalog:",
3100
+ typescript: "^5.7.3",
3101
+ vite: "^4.5.9",
3102
+ vitest: "^0.32.4",
3103
+ zod: "catalog:"
3143
3104
  }
3144
3105
  };
3145
3106
 
@@ -3210,8 +3171,8 @@ var _buildFragments = async (builder, tinaSchema) => {
3210
3171
  const fragDoc = {
3211
3172
  kind: "Document",
3212
3173
  definitions: uniqBy2(
3213
- // @ts-ignore
3214
3174
  extractInlineTypes(fragmentDefinitionsFields),
3175
+ // @ts-ignore - all nodes returned by extractInlineTypes have a name property
3215
3176
  (node) => node.name.value
3216
3177
  )
3217
3178
  };
@@ -3243,8 +3204,8 @@ var _buildQueries = async (builder, tinaSchema) => {
3243
3204
  const queryDoc = {
3244
3205
  kind: "Document",
3245
3206
  definitions: uniqBy2(
3246
- // @ts-ignore
3247
3207
  extractInlineTypes(operationsDefinitions),
3208
+ // @ts-ignore - all nodes returned by extractInlineTypes have a name property
3248
3209
  (node) => node.name.value
3249
3210
  )
3250
3211
  };
@@ -3295,7 +3256,9 @@ var _buildSchema = async (builder, tinaSchema) => {
3295
3256
  await builder.buildCreateCollectionFolderMutation()
3296
3257
  );
3297
3258
  await sequential(collections, async (collection) => {
3298
- queryTypeDefinitionFields.push(await builder.collectionDocument(collection));
3259
+ queryTypeDefinitionFields.push(
3260
+ await builder.collectionDocument(collection)
3261
+ );
3299
3262
  if (collection.isAuthCollection) {
3300
3263
  queryTypeDefinitionFields.push(
3301
3264
  await builder.authenticationCollectionDocument(collection)
@@ -3329,14 +3292,15 @@ var _buildSchema = async (builder, tinaSchema) => {
3329
3292
  fields: mutationTypeDefinitionFields
3330
3293
  })
3331
3294
  );
3332
- return {
3295
+ const schema = {
3333
3296
  kind: "Document",
3334
3297
  definitions: uniqBy2(
3335
- // @ts-ignore
3336
3298
  extractInlineTypes(definitions),
3299
+ // @ts-ignore - all nodes returned by extractInlineTypes have a name property
3337
3300
  (node) => node.name.value
3338
3301
  )
3339
3302
  };
3303
+ return schema;
3340
3304
  };
3341
3305
 
3342
3306
  // src/resolve.ts
@@ -3345,402 +3309,165 @@ import { graphql, buildASTSchema, getNamedType, GraphQLError as GraphQLError4 }
3345
3309
  // src/resolver/index.ts
3346
3310
  import path3 from "path";
3347
3311
  import isValid from "date-fns/isValid/index.js";
3312
+ import { JSONPath as JSONPath2 } from "jsonpath-plus";
3348
3313
 
3349
3314
  // src/mdx/index.ts
3350
- import { parseMDX, stringifyMDX } from "@tinacms/mdx";
3315
+ import { parseMDX, serializeMDX } from "@tinacms/mdx";
3351
3316
 
3352
3317
  // src/resolver/index.ts
3353
- import { JSONPath as JSONPath2 } from "jsonpath-plus";
3318
+ import { GraphQLError as GraphQLError2 } from "graphql";
3354
3319
 
3355
- // src/resolver/error.ts
3356
- var TinaGraphQLError = class extends Error {
3357
- constructor(message, extensions) {
3358
- super(message);
3359
- if (!this.name) {
3360
- Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
3361
- }
3362
- this.extensions = { ...extensions };
3363
- }
3320
+ // src/database/datalayer.ts
3321
+ import { JSONPath } from "jsonpath-plus";
3322
+ import sha from "js-sha1";
3323
+
3324
+ // src/database/level.ts
3325
+ var ARRAY_ITEM_VALUE_SEPARATOR = ",";
3326
+ var INDEX_KEY_FIELD_SEPARATOR = "";
3327
+ var CONTENT_ROOT_PREFIX = "~";
3328
+ var SUBLEVEL_OPTIONS = {
3329
+ separator: INDEX_KEY_FIELD_SEPARATOR,
3330
+ valueEncoding: "json"
3364
3331
  };
3365
- var TinaFetchError = class extends Error {
3366
- constructor(message, args) {
3367
- super(message);
3368
- this.name = "TinaFetchError";
3369
- this.collection = args.collection;
3370
- this.stack = args.stack;
3371
- this.file = args.file;
3372
- this.originalError = args.originalError;
3332
+ var LevelProxyHandler = {
3333
+ get: function(target, property) {
3334
+ if (!target[property]) {
3335
+ throw new Error(`The property, ${property.toString()}, doesn't exist`);
3336
+ }
3337
+ if (typeof target[property] !== "function") {
3338
+ throw new Error(
3339
+ `The property, ${property.toString()}, is not a function`
3340
+ );
3341
+ }
3342
+ if (property === "get") {
3343
+ return async (...args) => {
3344
+ let result;
3345
+ try {
3346
+ result = await target[property].apply(target, args);
3347
+ } catch (e) {
3348
+ if (e.code !== "LEVEL_NOT_FOUND") {
3349
+ throw e;
3350
+ }
3351
+ }
3352
+ return result;
3353
+ };
3354
+ } else if (property === "sublevel") {
3355
+ return (...args) => {
3356
+ return new Proxy(
3357
+ // eslint-disable-next-line prefer-spread
3358
+ target[property].apply(target, args),
3359
+ LevelProxyHandler
3360
+ );
3361
+ };
3362
+ } else {
3363
+ return (...args) => target[property].apply(target, args);
3364
+ }
3373
3365
  }
3374
3366
  };
3375
- var TinaQueryError = class extends TinaFetchError {
3376
- constructor(args) {
3377
- super(
3378
- `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3379
- args
3380
- );
3367
+ var LevelProxy = class {
3368
+ constructor(level) {
3369
+ return new Proxy(level, LevelProxyHandler);
3381
3370
  }
3382
3371
  };
3383
- var TinaParseDocumentError = class extends TinaFetchError {
3384
- constructor(args) {
3385
- super(
3386
- `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3387
- args
3372
+
3373
+ // src/database/datalayer.ts
3374
+ import path2 from "path";
3375
+
3376
+ // src/database/util.ts
3377
+ import toml from "@iarna/toml";
3378
+ import {
3379
+ normalizePath
3380
+ } from "@tinacms/schema-tools";
3381
+ import matter from "gray-matter";
3382
+ import yaml from "js-yaml";
3383
+ import path from "path";
3384
+ import micromatch from "micromatch";
3385
+
3386
+ // src/database/alias-utils.ts
3387
+ var replaceBlockAliases = (template, item) => {
3388
+ const output = { ...item };
3389
+ const templateKey = template.templateKey || "_template";
3390
+ const templateName = output[templateKey];
3391
+ const matchingTemplate = template.templates.find(
3392
+ (t) => t.nameOverride == templateName || t.name == templateName
3393
+ );
3394
+ if (!matchingTemplate) {
3395
+ throw new Error(
3396
+ `Block template "${templateName}" is not defined for field "${template.name}"`
3388
3397
  );
3389
3398
  }
3390
- toString() {
3391
- return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
3399
+ output._template = matchingTemplate.name;
3400
+ if (templateKey != "_template") {
3401
+ delete output[templateKey];
3392
3402
  }
3403
+ return output;
3393
3404
  };
3394
- var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
3395
- var handleFetchErrorError = (e, verbose) => {
3396
- if (e instanceof Error) {
3397
- if (e instanceof TinaFetchError) {
3398
- if (verbose) {
3399
- console.log(e.toString());
3400
- console.log(e);
3401
- console.log(e.stack);
3405
+ var replaceNameOverrides = (template, obj) => {
3406
+ if (template.list) {
3407
+ return obj.map((item) => {
3408
+ if (isBlockField(template)) {
3409
+ item = replaceBlockAliases(template, item);
3402
3410
  }
3403
- }
3411
+ return _replaceNameOverrides(
3412
+ getTemplateForData(template, item).fields,
3413
+ item
3414
+ );
3415
+ });
3404
3416
  } else {
3405
- console.error(e);
3417
+ return _replaceNameOverrides(getTemplateForData(template, obj).fields, obj);
3406
3418
  }
3407
- throw e;
3408
3419
  };
3409
-
3410
- // src/resolver/filter-utils.ts
3411
- var resolveReferences = async (filter, fields, resolver) => {
3412
- for (const fieldKey of Object.keys(filter)) {
3413
- const fieldDefinition = fields.find(
3414
- (f) => f.name === fieldKey
3420
+ function isBlockField(field) {
3421
+ return field && field.type === "object" && field.templates?.length > 0;
3422
+ }
3423
+ var _replaceNameOverrides = (fields, obj) => {
3424
+ const output = {};
3425
+ Object.keys(obj).forEach((key) => {
3426
+ const field = fields.find(
3427
+ (fieldWithMatchingAlias) => (fieldWithMatchingAlias?.nameOverride || fieldWithMatchingAlias?.name) === key
3415
3428
  );
3416
- if (fieldDefinition) {
3417
- if (fieldDefinition.type === "reference") {
3418
- const { edges, values } = await resolver(filter, fieldDefinition);
3419
- if (edges.length === 1) {
3420
- filter[fieldKey] = {
3421
- eq: values[0]
3422
- };
3423
- } else if (edges.length > 1) {
3424
- filter[fieldKey] = {
3425
- in: values
3426
- };
3427
- } else {
3428
- filter[fieldKey] = {
3429
- eq: "___null___"
3430
- };
3431
- }
3432
- } else if (fieldDefinition.type === "object") {
3433
- if (fieldDefinition.templates) {
3434
- for (const templateName of Object.keys(filter[fieldKey])) {
3435
- const template = fieldDefinition.templates.find(
3436
- (template2) => !(typeof template2 === "string") && template2.name === templateName
3437
- );
3438
- if (template) {
3439
- await resolveReferences(
3440
- filter[fieldKey][templateName],
3441
- template.fields,
3442
- resolver
3443
- );
3444
- } else {
3445
- throw new Error(`Template ${templateName} not found`);
3446
- }
3447
- }
3448
- } else {
3449
- await resolveReferences(
3450
- filter[fieldKey],
3451
- fieldDefinition.fields,
3452
- resolver
3453
- );
3454
- }
3429
+ output[field?.name || key] = field?.type == "object" ? replaceNameOverrides(field, obj[key]) : obj[key];
3430
+ });
3431
+ return output;
3432
+ };
3433
+ var getTemplateForData = (field, data) => {
3434
+ if (field.templates?.length) {
3435
+ const templateKey = "_template";
3436
+ if (data[templateKey]) {
3437
+ const result = field.templates.find(
3438
+ (template) => template.nameOverride === data[templateKey] || template.name === data[templateKey]
3439
+ );
3440
+ if (result) {
3441
+ return result;
3455
3442
  }
3456
- } else {
3457
- throw new Error(`Unable to find field ${fieldKey}`);
3443
+ throw new Error(
3444
+ `Template "${data[templateKey]}" is not defined for field "${field.name}"`
3445
+ );
3458
3446
  }
3447
+ throw new Error(
3448
+ `Missing required key "${templateKey}" on field "${field.name}"`
3449
+ );
3450
+ } else {
3451
+ return field;
3459
3452
  }
3460
3453
  };
3461
- var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
3462
- for (const childFieldName of Object.keys(filterNode)) {
3463
- const childField = fields.find((field) => field.name === childFieldName);
3464
- if (!childField) {
3465
- throw new Error(`Unable to find type for field ${childFieldName}`);
3466
- }
3467
- collectConditionsForField(
3468
- childFieldName,
3469
- childField,
3470
- filterNode[childFieldName],
3471
- pathExpression,
3472
- collectCondition
3454
+ var applyBlockAliases = (template, item) => {
3455
+ const output = { ...item };
3456
+ const templateKey = template.templateKey || "_template";
3457
+ const templateName = output._template;
3458
+ const matchingTemplate = template.templates.find(
3459
+ (t) => t.nameOverride == templateName || t.name == templateName
3460
+ );
3461
+ if (!matchingTemplate) {
3462
+ throw new Error(
3463
+ `Block template "${templateName}" is not defined for field "${template.name}"`
3473
3464
  );
3474
3465
  }
3475
- };
3476
- var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3477
- if (field.list && field.templates) {
3478
- for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
3479
- const template = field.templates.find(
3480
- (template2) => !(typeof template2 === "string") && template2.name === filterKey
3481
- );
3482
- const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
3483
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
3484
- collectConditionsForChildFields(
3485
- childFilterNode,
3486
- template.fields,
3487
- filterPath,
3488
- collectCondition
3489
- );
3490
- }
3491
- } else {
3492
- const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
3493
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
3494
- collectConditionsForChildFields(
3495
- filterNode,
3496
- field.fields,
3497
- filterPath,
3498
- collectCondition
3499
- );
3500
- }
3501
- };
3502
- var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3503
- if (field.type === "object") {
3504
- collectConditionsForObjectField(
3505
- fieldName,
3506
- field,
3507
- filterNode,
3508
- pathExpression,
3509
- collectCondition
3510
- );
3511
- } else {
3512
- collectCondition({
3513
- filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
3514
- filterExpression: {
3515
- _type: field.type,
3516
- _list: !!field.list,
3517
- ...filterNode
3518
- }
3519
- });
3520
- }
3521
- };
3522
-
3523
- // src/resolver/media-utils.ts
3524
- var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
3525
- if (config && value) {
3526
- if (config.useRelativeMedia === true) {
3527
- return value;
3528
- }
3529
- if (hasTinaMediaConfig(schema) === true) {
3530
- const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
3531
- if (typeof value === "string" && value.includes(assetsURL)) {
3532
- const cleanMediaRoot = cleanUpSlashes(
3533
- schema.config.media.tina.mediaRoot
3534
- );
3535
- const strippedURL = value.replace(assetsURL, "");
3536
- return `${cleanMediaRoot}${strippedURL}`;
3537
- }
3538
- if (Array.isArray(value)) {
3539
- return value.map((v) => {
3540
- if (!v || typeof v !== "string") return v;
3541
- const cleanMediaRoot = cleanUpSlashes(
3542
- schema.config.media.tina.mediaRoot
3543
- );
3544
- const strippedURL = v.replace(assetsURL, "");
3545
- return `${cleanMediaRoot}${strippedURL}`;
3546
- });
3547
- }
3548
- return value;
3549
- }
3550
- return value;
3551
- } else {
3552
- return value;
3553
- }
3554
- };
3555
- var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
3556
- if (config && value) {
3557
- if (config.useRelativeMedia === true) {
3558
- return value;
3559
- }
3560
- if (hasTinaMediaConfig(schema) === true) {
3561
- const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
3562
- if (typeof value === "string") {
3563
- const strippedValue = value.replace(cleanMediaRoot, "");
3564
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3565
- }
3566
- if (Array.isArray(value)) {
3567
- return value.map((v) => {
3568
- if (!v || typeof v !== "string") return v;
3569
- const strippedValue = v.replace(cleanMediaRoot, "");
3570
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3571
- });
3572
- }
3573
- }
3574
- return value;
3575
- } else {
3576
- return value;
3577
- }
3578
- };
3579
- var cleanUpSlashes = (path7) => {
3580
- if (path7) {
3581
- return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
3582
- }
3583
- return "";
3584
- };
3585
- var hasTinaMediaConfig = (schema) => {
3586
- if (!schema.config?.media?.tina) return false;
3587
- if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
3588
- return false;
3589
- return true;
3590
- };
3591
-
3592
- // src/resolver/index.ts
3593
- import { GraphQLError as GraphQLError2 } from "graphql";
3594
-
3595
- // src/database/datalayer.ts
3596
- import { JSONPath } from "jsonpath-plus";
3597
- import sha from "js-sha1";
3598
-
3599
- // src/database/level.ts
3600
- var ARRAY_ITEM_VALUE_SEPARATOR = ",";
3601
- var INDEX_KEY_FIELD_SEPARATOR = "";
3602
- var CONTENT_ROOT_PREFIX = "~";
3603
- var SUBLEVEL_OPTIONS = {
3604
- separator: INDEX_KEY_FIELD_SEPARATOR,
3605
- valueEncoding: "json"
3606
- };
3607
- var LevelProxyHandler = {
3608
- get: function(target, property) {
3609
- if (!target[property]) {
3610
- throw new Error(`The property, ${property.toString()}, doesn't exist`);
3611
- }
3612
- if (typeof target[property] !== "function") {
3613
- throw new Error(`The property, ${property.toString()}, is not a function`);
3614
- }
3615
- if (property === "get") {
3616
- return async (...args) => {
3617
- let result;
3618
- try {
3619
- result = await target[property].apply(target, args);
3620
- } catch (e) {
3621
- if (e.code !== "LEVEL_NOT_FOUND") {
3622
- throw e;
3623
- }
3624
- }
3625
- return result;
3626
- };
3627
- } else if (property === "sublevel") {
3628
- return (...args) => {
3629
- return new Proxy(
3630
- // eslint-disable-next-line prefer-spread
3631
- target[property].apply(target, args),
3632
- LevelProxyHandler
3633
- );
3634
- };
3635
- } else {
3636
- return (...args) => target[property].apply(target, args);
3637
- }
3638
- }
3639
- };
3640
- var LevelProxy = class {
3641
- constructor(level) {
3642
- return new Proxy(level, LevelProxyHandler);
3643
- }
3644
- };
3645
-
3646
- // src/database/datalayer.ts
3647
- import path2 from "path";
3648
-
3649
- // src/database/util.ts
3650
- import toml from "@iarna/toml";
3651
- import yaml from "js-yaml";
3652
- import matter from "gray-matter";
3653
- import {
3654
- normalizePath
3655
- } from "@tinacms/schema-tools";
3656
- import micromatch from "micromatch";
3657
- import path from "path";
3658
-
3659
- // src/database/alias-utils.ts
3660
- var replaceBlockAliases = (template, item) => {
3661
- const output = { ...item };
3662
- const templateKey = template.templateKey || "_template";
3663
- const templateName = output[templateKey];
3664
- const matchingTemplate = template.templates.find(
3665
- (t) => t.nameOverride == templateName || t.name == templateName
3666
- );
3667
- if (!matchingTemplate) {
3668
- throw new Error(
3669
- `Block template "${templateName}" is not defined for field "${template.name}"`
3670
- );
3671
- }
3672
- output._template = matchingTemplate.name;
3673
- if (templateKey != "_template") {
3674
- delete output[templateKey];
3675
- }
3676
- return output;
3677
- };
3678
- var replaceNameOverrides = (template, obj) => {
3679
- if (template.list) {
3680
- return obj.map((item) => {
3681
- if (isBlockField(template)) {
3682
- item = replaceBlockAliases(template, item);
3683
- }
3684
- return _replaceNameOverrides(
3685
- getTemplateForData(template, item).fields,
3686
- item
3687
- );
3688
- });
3689
- } else {
3690
- return _replaceNameOverrides(getTemplateForData(template, obj).fields, obj);
3691
- }
3692
- };
3693
- function isBlockField(field) {
3694
- return field && field.type === "object" && field.templates?.length > 0;
3695
- }
3696
- var _replaceNameOverrides = (fields, obj) => {
3697
- const output = {};
3698
- Object.keys(obj).forEach((key) => {
3699
- const field = fields.find(
3700
- (fieldWithMatchingAlias) => (fieldWithMatchingAlias?.nameOverride || fieldWithMatchingAlias?.name) === key
3701
- );
3702
- output[field?.name || key] = field?.type == "object" ? replaceNameOverrides(field, obj[key]) : obj[key];
3703
- });
3704
- return output;
3705
- };
3706
- var getTemplateForData = (field, data) => {
3707
- if (field.templates?.length) {
3708
- const templateKey = "_template";
3709
- if (data[templateKey]) {
3710
- const result = field.templates.find(
3711
- (template) => template.nameOverride === data[templateKey] || template.name === data[templateKey]
3712
- );
3713
- if (result) {
3714
- return result;
3715
- }
3716
- throw new Error(
3717
- `Template "${data[templateKey]}" is not defined for field "${field.name}"`
3718
- );
3719
- }
3720
- throw new Error(
3721
- `Missing required key "${templateKey}" on field "${field.name}"`
3722
- );
3723
- } else {
3724
- return field;
3725
- }
3726
- };
3727
- var applyBlockAliases = (template, item) => {
3728
- const output = { ...item };
3729
- const templateKey = template.templateKey || "_template";
3730
- const templateName = output._template;
3731
- const matchingTemplate = template.templates.find(
3732
- (t) => t.nameOverride == templateName || t.name == templateName
3733
- );
3734
- if (!matchingTemplate) {
3735
- throw new Error(
3736
- `Block template "${templateName}" is not defined for field "${template.name}"`
3737
- );
3738
- }
3739
- output[templateKey] = matchingTemplate.nameOverride || matchingTemplate.name;
3740
- if (templateKey != "_template") {
3741
- delete output._template;
3742
- }
3743
- return output;
3466
+ output[templateKey] = matchingTemplate.nameOverride || matchingTemplate.name;
3467
+ if (templateKey != "_template") {
3468
+ delete output._template;
3469
+ }
3470
+ return output;
3744
3471
  };
3745
3472
  var applyNameOverrides = (template, obj) => {
3746
3473
  if (template.list) {
@@ -3995,6 +3722,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3995
3722
 
3996
3723
  // src/database/datalayer.ts
3997
3724
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
3725
+ var REFS_COLLECTIONS_SORT_KEY = "__refs__";
3726
+ var REFS_REFERENCE_FIELD = "__tina_ref__";
3727
+ var REFS_PATH_FIELD = "__tina_ref_path__";
3998
3728
  var DEFAULT_NUMERIC_LPAD = 4;
3999
3729
  var applyPadding = (input, pad) => {
4000
3730
  if (pad) {
@@ -4568,6 +4298,57 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4568
4298
  }
4569
4299
  return result;
4570
4300
  };
4301
+ var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4302
+ const result = [];
4303
+ if (collection) {
4304
+ for (const [c, referencePaths] of Object.entries(references || {})) {
4305
+ if (!referencePaths.length) {
4306
+ continue;
4307
+ }
4308
+ const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4309
+ const refSublevel = collectionSublevel.sublevel(
4310
+ REFS_COLLECTIONS_SORT_KEY,
4311
+ SUBLEVEL_OPTIONS
4312
+ );
4313
+ const references2 = {};
4314
+ for (const path7 of referencePaths) {
4315
+ const ref = JSONPath({ path: path7, json: data });
4316
+ if (!ref) {
4317
+ continue;
4318
+ }
4319
+ if (Array.isArray(ref)) {
4320
+ for (const r of ref) {
4321
+ if (!r) {
4322
+ continue;
4323
+ }
4324
+ if (references2[r]) {
4325
+ references2[r].push(path7);
4326
+ } else {
4327
+ references2[r] = [path7];
4328
+ }
4329
+ }
4330
+ } else {
4331
+ if (references2[ref]) {
4332
+ references2[ref].push(path7);
4333
+ } else {
4334
+ references2[ref] = [path7];
4335
+ }
4336
+ }
4337
+ }
4338
+ for (const ref of Object.keys(references2)) {
4339
+ for (const path7 of references2[ref]) {
4340
+ result.push({
4341
+ type: opType,
4342
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4343
+ sublevel: refSublevel,
4344
+ value: opType === "put" ? {} : void 0
4345
+ });
4346
+ }
4347
+ }
4348
+ }
4349
+ }
4350
+ return result;
4351
+ };
4571
4352
  var makeStringEscaper = (regex, replacement) => {
4572
4353
  return (input) => {
4573
4354
  if (Array.isArray(input)) {
@@ -4581,12 +4362,248 @@ var makeStringEscaper = (regex, replacement) => {
4581
4362
  return input;
4582
4363
  }
4583
4364
  }
4584
- };
4365
+ };
4366
+ };
4367
+ var stringEscaper = makeStringEscaper(
4368
+ new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4369
+ encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4370
+ );
4371
+
4372
+ // src/resolver/error.ts
4373
+ var TinaGraphQLError = class extends Error {
4374
+ constructor(message, extensions) {
4375
+ super(message);
4376
+ if (!this.name) {
4377
+ Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
4378
+ }
4379
+ this.extensions = { ...extensions };
4380
+ }
4381
+ };
4382
+ var TinaFetchError = class extends Error {
4383
+ constructor(message, args) {
4384
+ super(message);
4385
+ this.name = "TinaFetchError";
4386
+ this.collection = args.collection;
4387
+ this.file = args.file;
4388
+ this.originalError = args.originalError;
4389
+ }
4390
+ };
4391
+ var TinaQueryError = class extends TinaFetchError {
4392
+ constructor(args) {
4393
+ super(
4394
+ `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4395
+ args
4396
+ );
4397
+ }
4398
+ };
4399
+ var TinaParseDocumentError = class extends TinaFetchError {
4400
+ constructor(args) {
4401
+ super(
4402
+ `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4403
+ args
4404
+ );
4405
+ }
4406
+ toString() {
4407
+ return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
4408
+ }
4409
+ };
4410
+ var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
4411
+ var handleFetchErrorError = (e, verbose) => {
4412
+ if (e instanceof Error) {
4413
+ if (e instanceof TinaFetchError) {
4414
+ if (verbose) {
4415
+ console.log(e.toString());
4416
+ console.log(e);
4417
+ console.log(e.stack);
4418
+ }
4419
+ }
4420
+ } else {
4421
+ console.error(e);
4422
+ }
4423
+ throw e;
4424
+ };
4425
+
4426
+ // src/resolver/filter-utils.ts
4427
+ var resolveReferences = async (filter, fields, resolver) => {
4428
+ for (const fieldKey of Object.keys(filter)) {
4429
+ const fieldDefinition = fields.find(
4430
+ (f) => f.name === fieldKey
4431
+ );
4432
+ if (fieldDefinition) {
4433
+ if (fieldDefinition.type === "reference") {
4434
+ const { edges, values } = await resolver(filter, fieldDefinition);
4435
+ if (edges.length === 1) {
4436
+ filter[fieldKey] = {
4437
+ eq: values[0]
4438
+ };
4439
+ } else if (edges.length > 1) {
4440
+ filter[fieldKey] = {
4441
+ in: values
4442
+ };
4443
+ } else {
4444
+ filter[fieldKey] = {
4445
+ eq: "___null___"
4446
+ };
4447
+ }
4448
+ } else if (fieldDefinition.type === "object") {
4449
+ if (fieldDefinition.templates) {
4450
+ for (const templateName of Object.keys(filter[fieldKey])) {
4451
+ const template = fieldDefinition.templates.find(
4452
+ (template2) => !(typeof template2 === "string") && template2.name === templateName
4453
+ );
4454
+ if (template) {
4455
+ await resolveReferences(
4456
+ filter[fieldKey][templateName],
4457
+ template.fields,
4458
+ resolver
4459
+ );
4460
+ } else {
4461
+ throw new Error(`Template ${templateName} not found`);
4462
+ }
4463
+ }
4464
+ } else {
4465
+ await resolveReferences(
4466
+ filter[fieldKey],
4467
+ fieldDefinition.fields,
4468
+ resolver
4469
+ );
4470
+ }
4471
+ }
4472
+ } else {
4473
+ throw new Error(`Unable to find field ${fieldKey}`);
4474
+ }
4475
+ }
4476
+ };
4477
+ var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
4478
+ for (const childFieldName of Object.keys(filterNode)) {
4479
+ const childField = fields.find((field) => field.name === childFieldName);
4480
+ if (!childField) {
4481
+ throw new Error(`Unable to find type for field ${childFieldName}`);
4482
+ }
4483
+ collectConditionsForField(
4484
+ childFieldName,
4485
+ childField,
4486
+ filterNode[childFieldName],
4487
+ pathExpression,
4488
+ collectCondition
4489
+ );
4490
+ }
4491
+ };
4492
+ var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4493
+ if (field.list && field.templates) {
4494
+ for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
4495
+ const template = field.templates.find(
4496
+ (template2) => !(typeof template2 === "string") && template2.name === filterKey
4497
+ );
4498
+ const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
4499
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
4500
+ collectConditionsForChildFields(
4501
+ childFilterNode,
4502
+ template.fields,
4503
+ filterPath,
4504
+ collectCondition
4505
+ );
4506
+ }
4507
+ } else {
4508
+ const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
4509
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
4510
+ collectConditionsForChildFields(
4511
+ filterNode,
4512
+ field.fields,
4513
+ filterPath,
4514
+ collectCondition
4515
+ );
4516
+ }
4517
+ };
4518
+ var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4519
+ if (field.type === "object") {
4520
+ collectConditionsForObjectField(
4521
+ fieldName,
4522
+ field,
4523
+ filterNode,
4524
+ pathExpression,
4525
+ collectCondition
4526
+ );
4527
+ } else {
4528
+ collectCondition({
4529
+ filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
4530
+ filterExpression: {
4531
+ _type: field.type,
4532
+ _list: !!field.list,
4533
+ ...filterNode
4534
+ }
4535
+ });
4536
+ }
4537
+ };
4538
+
4539
+ // src/resolver/media-utils.ts
4540
+ var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
4541
+ if (config && value) {
4542
+ if (config.useRelativeMedia === true) {
4543
+ return value;
4544
+ }
4545
+ if (hasTinaMediaConfig(schema) === true) {
4546
+ const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
4547
+ if (typeof value === "string" && value.includes(assetsURL)) {
4548
+ const cleanMediaRoot = cleanUpSlashes(
4549
+ schema.config.media.tina.mediaRoot
4550
+ );
4551
+ const strippedURL = value.replace(assetsURL, "");
4552
+ return `${cleanMediaRoot}${strippedURL}`;
4553
+ }
4554
+ if (Array.isArray(value)) {
4555
+ return value.map((v) => {
4556
+ if (!v || typeof v !== "string") return v;
4557
+ const cleanMediaRoot = cleanUpSlashes(
4558
+ schema.config.media.tina.mediaRoot
4559
+ );
4560
+ const strippedURL = v.replace(assetsURL, "");
4561
+ return `${cleanMediaRoot}${strippedURL}`;
4562
+ });
4563
+ }
4564
+ return value;
4565
+ }
4566
+ return value;
4567
+ } else {
4568
+ return value;
4569
+ }
4570
+ };
4571
+ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
4572
+ if (config && value) {
4573
+ if (config.useRelativeMedia === true) {
4574
+ return value;
4575
+ }
4576
+ if (hasTinaMediaConfig(schema) === true) {
4577
+ const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
4578
+ if (typeof value === "string") {
4579
+ const strippedValue = value.replace(cleanMediaRoot, "");
4580
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4581
+ }
4582
+ if (Array.isArray(value)) {
4583
+ return value.map((v) => {
4584
+ if (!v || typeof v !== "string") return v;
4585
+ const strippedValue = v.replace(cleanMediaRoot, "");
4586
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4587
+ });
4588
+ }
4589
+ }
4590
+ return value;
4591
+ } else {
4592
+ return value;
4593
+ }
4594
+ };
4595
+ var cleanUpSlashes = (path7) => {
4596
+ if (path7) {
4597
+ return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
4598
+ }
4599
+ return "";
4600
+ };
4601
+ var hasTinaMediaConfig = (schema) => {
4602
+ if (!schema.config?.media?.tina) return false;
4603
+ if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
4604
+ return false;
4605
+ return true;
4585
4606
  };
4586
- var stringEscaper = makeStringEscaper(
4587
- new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4588
- encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4589
- );
4590
4607
 
4591
4608
  // src/resolver/index.ts
4592
4609
  var createResolver = (args) => {
@@ -4748,8 +4765,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4748
4765
  originalError: e,
4749
4766
  collection: collection.name,
4750
4767
  includeAuditMessage: !isAudit,
4751
- file: relativePath,
4752
- stack: e.stack
4768
+ file: relativePath
4753
4769
  });
4754
4770
  }
4755
4771
  const titleField = template.fields.find((x) => {
@@ -4788,24 +4804,33 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4788
4804
  throw e;
4789
4805
  }
4790
4806
  };
4791
- var updateObjectWithJsonPath = (obj, path7, newValue) => {
4807
+ var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4808
+ let updated = false;
4792
4809
  if (!path7.includes(".") && !path7.includes("[")) {
4793
- if (path7 in obj) {
4810
+ if (path7 in obj && obj[path7] === oldValue) {
4794
4811
  obj[path7] = newValue;
4812
+ updated = true;
4795
4813
  }
4796
- return obj;
4797
- }
4798
- const parentPath = path7.replace(/\.[^.]+$/, "");
4799
- const keyToUpdate = path7.match(/[^.]+$/)[0];
4800
- const parents = JSONPath2({ path: parentPath, json: obj, resultType: "value" });
4814
+ return { object: obj, updated };
4815
+ }
4816
+ const parentPath = path7.replace(/\.[^.\[\]]+$/, "");
4817
+ const keyToUpdate = path7.match(/[^.\[\]]+$/)[0];
4818
+ const parents = JSONPath2({
4819
+ path: parentPath,
4820
+ json: obj,
4821
+ resultType: "value"
4822
+ });
4801
4823
  if (parents.length > 0) {
4802
4824
  parents.forEach((parent) => {
4803
4825
  if (parent && typeof parent === "object" && keyToUpdate in parent) {
4804
- parent[keyToUpdate] = newValue;
4826
+ if (parent[keyToUpdate] === oldValue) {
4827
+ parent[keyToUpdate] = newValue;
4828
+ updated = true;
4829
+ }
4805
4830
  }
4806
4831
  });
4807
4832
  }
4808
- return obj;
4833
+ return { object: obj, updated };
4809
4834
  };
4810
4835
  var Resolver = class {
4811
4836
  constructor(init) {
@@ -4822,7 +4847,9 @@ var Resolver = class {
4822
4847
  };
4823
4848
  this.getRaw = async (fullPath) => {
4824
4849
  if (typeof fullPath !== "string") {
4825
- throw new Error(`fullPath must be of type string for getDocument request`);
4850
+ throw new Error(
4851
+ `fullPath must be of type string for getDocument request`
4852
+ );
4826
4853
  }
4827
4854
  return this.database.get(fullPath);
4828
4855
  };
@@ -4851,7 +4878,9 @@ var Resolver = class {
4851
4878
  };
4852
4879
  this.getDocument = async (fullPath, opts = {}) => {
4853
4880
  if (typeof fullPath !== "string") {
4854
- throw new Error(`fullPath must be of type string for getDocument request`);
4881
+ throw new Error(
4882
+ `fullPath must be of type string for getDocument request`
4883
+ );
4855
4884
  }
4856
4885
  const rawData = await this.getRaw(fullPath);
4857
4886
  const hasReferences = opts?.checkReferences ? await this.hasReferences(fullPath, opts.collection) : void 0;
@@ -4866,7 +4895,9 @@ var Resolver = class {
4866
4895
  };
4867
4896
  this.deleteDocument = async (fullPath) => {
4868
4897
  if (typeof fullPath !== "string") {
4869
- throw new Error(`fullPath must be of type string for getDocument request`);
4898
+ throw new Error(
4899
+ `fullPath must be of type string for getDocument request`
4900
+ );
4870
4901
  }
4871
4902
  await this.database.delete(fullPath);
4872
4903
  };
@@ -5070,7 +5101,11 @@ var Resolver = class {
5070
5101
  collection,
5071
5102
  doc?._rawData
5072
5103
  );
5073
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
5104
+ await this.database.put(
5105
+ realPath,
5106
+ { ...oldDoc, ...params },
5107
+ collection.name
5108
+ );
5074
5109
  return this.getDocument(realPath);
5075
5110
  };
5076
5111
  /**
@@ -5183,17 +5218,35 @@ var Resolver = class {
5183
5218
  await this.deleteDocument(realPath);
5184
5219
  if (await this.hasReferences(realPath, collection)) {
5185
5220
  const collRefs = await this.findReferences(realPath, collection);
5186
- for (const [collection2, refFields] of Object.entries(collRefs)) {
5187
- for (const [refPath, refs] of Object.entries(refFields)) {
5188
- let refDoc = await this.getRaw(refPath);
5189
- for (const ref of refs) {
5190
- refDoc = updateObjectWithJsonPath(
5221
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5222
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5223
+ docsWithRefs
5224
+ )) {
5225
+ let refDoc = await this.getRaw(pathToDocWithRef);
5226
+ let hasUpdate = false;
5227
+ for (const path7 of referencePaths) {
5228
+ const { object: object2, updated } = updateObjectWithJsonPath(
5191
5229
  refDoc,
5192
- ref.path.join("."),
5230
+ path7,
5231
+ realPath,
5193
5232
  null
5194
5233
  );
5234
+ refDoc = object2;
5235
+ hasUpdate = updated || hasUpdate;
5236
+ }
5237
+ if (hasUpdate) {
5238
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5239
+ if (!collectionWithRef) {
5240
+ throw new Error(
5241
+ `Unable to find collection for ${pathToDocWithRef}`
5242
+ );
5243
+ }
5244
+ await this.database.put(
5245
+ pathToDocWithRef,
5246
+ refDoc,
5247
+ collectionWithRef.name
5248
+ );
5195
5249
  }
5196
- await this.database.put(refPath, refDoc, collection2);
5197
5250
  }
5198
5251
  }
5199
5252
  }
@@ -5213,26 +5266,49 @@ var Resolver = class {
5213
5266
  collection?.path,
5214
5267
  args.params.relativePath
5215
5268
  );
5269
+ if (newRealPath === realPath) {
5270
+ return doc;
5271
+ }
5216
5272
  await this.database.put(newRealPath, doc._rawData, collection.name);
5217
5273
  await this.deleteDocument(realPath);
5218
5274
  const collRefs = await this.findReferences(realPath, collection);
5219
- for (const [collection2, refFields] of Object.entries(collRefs)) {
5220
- for (const [refPath, refs] of Object.entries(refFields)) {
5221
- let refDoc = await this.getRaw(refPath);
5222
- for (const ref of refs) {
5223
- refDoc = updateObjectWithJsonPath(
5224
- refDoc,
5225
- ref.path.join("."),
5275
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5276
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5277
+ docsWithRefs
5278
+ )) {
5279
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5280
+ let hasUpdate = false;
5281
+ for (const path7 of referencePaths) {
5282
+ const { object: object2, updated } = updateObjectWithJsonPath(
5283
+ docWithRef,
5284
+ path7,
5285
+ realPath,
5226
5286
  newRealPath
5227
5287
  );
5288
+ docWithRef = object2;
5289
+ hasUpdate = updated || hasUpdate;
5290
+ }
5291
+ if (hasUpdate) {
5292
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5293
+ if (!collectionWithRef) {
5294
+ throw new Error(
5295
+ `Unable to find collection for ${pathToDocWithRef}`
5296
+ );
5297
+ }
5298
+ await this.database.put(
5299
+ pathToDocWithRef,
5300
+ docWithRef,
5301
+ collectionWithRef.name
5302
+ );
5228
5303
  }
5229
- await this.database.put(refPath, refDoc, collection2);
5230
5304
  }
5231
5305
  }
5232
5306
  return this.getDocument(newRealPath);
5233
5307
  }
5234
5308
  if (alreadyExists === false) {
5235
- throw new Error(`Unable to update document, ${realPath} does not exist`);
5309
+ throw new Error(
5310
+ `Unable to update document, ${realPath} does not exist`
5311
+ );
5236
5312
  }
5237
5313
  return this.updateResolveDocument({
5238
5314
  collection,
@@ -5358,35 +5434,30 @@ var Resolver = class {
5358
5434
  */
5359
5435
  this.hasReferences = async (id, c) => {
5360
5436
  let count = 0;
5361
- const deepRefs = this.tinaSchema.findReferences(c.name);
5362
- for (const [collection, refs] of Object.entries(deepRefs)) {
5363
- for (const ref of refs) {
5364
- await this.database.query(
5365
- {
5366
- collection,
5367
- filterChain: makeFilterChain({
5368
- conditions: [
5369
- {
5370
- filterPath: ref.path.join("."),
5371
- filterExpression: {
5372
- _type: "reference",
5373
- _list: false,
5374
- eq: id
5375
- }
5376
- }
5377
- ]
5378
- }),
5379
- sort: ref.field.name
5380
- },
5381
- (refId) => {
5382
- count++;
5383
- return refId;
5384
- }
5385
- );
5386
- if (count) {
5387
- return true;
5388
- }
5437
+ await this.database.query(
5438
+ {
5439
+ collection: c.name,
5440
+ filterChain: makeFilterChain({
5441
+ conditions: [
5442
+ {
5443
+ filterPath: REFS_REFERENCE_FIELD,
5444
+ filterExpression: {
5445
+ _type: "string",
5446
+ _list: false,
5447
+ eq: id
5448
+ }
5449
+ }
5450
+ ]
5451
+ }),
5452
+ sort: REFS_COLLECTIONS_SORT_KEY
5453
+ },
5454
+ (refId) => {
5455
+ count++;
5456
+ return refId;
5389
5457
  }
5458
+ );
5459
+ if (count) {
5460
+ return true;
5390
5461
  }
5391
5462
  return false;
5392
5463
  };
@@ -5394,46 +5465,41 @@ var Resolver = class {
5394
5465
  * Finds references to a document
5395
5466
  * @param id the id of the document to find references to
5396
5467
  * @param c the collection to find references in
5397
- * @returns references to the document in the form of a map of collection names to a list of fields that reference the document
5468
+ * @returns a map of references to the document
5398
5469
  */
5399
5470
  this.findReferences = async (id, c) => {
5400
5471
  const references = {};
5401
- const deepRefs = this.tinaSchema.findReferences(c.name);
5402
- for (const [collection, refs] of Object.entries(deepRefs)) {
5403
- for (const ref of refs) {
5404
- await this.database.query(
5405
- {
5406
- collection,
5407
- filterChain: makeFilterChain({
5408
- conditions: [
5409
- {
5410
- filterPath: ref.path.join("."),
5411
- filterExpression: {
5412
- _type: "reference",
5413
- _list: false,
5414
- eq: id
5415
- }
5416
- }
5417
- ]
5418
- }),
5419
- sort: ref.field.name
5420
- },
5421
- (refId) => {
5422
- if (!references[collection]) {
5423
- references[collection] = {};
5424
- }
5425
- if (!references[collection][refId]) {
5426
- references[collection][refId] = [];
5472
+ await this.database.query(
5473
+ {
5474
+ collection: c.name,
5475
+ filterChain: makeFilterChain({
5476
+ conditions: [
5477
+ {
5478
+ filterPath: REFS_REFERENCE_FIELD,
5479
+ filterExpression: {
5480
+ _type: "string",
5481
+ _list: false,
5482
+ eq: id
5483
+ }
5427
5484
  }
5428
- references[collection][refId].push({
5429
- path: ref.path,
5430
- field: ref.field
5431
- });
5432
- return refId;
5433
- }
5434
- );
5485
+ ]
5486
+ }),
5487
+ sort: REFS_COLLECTIONS_SORT_KEY
5488
+ },
5489
+ (refId, rawItem) => {
5490
+ if (!references[c.name]) {
5491
+ references[c.name] = {};
5492
+ }
5493
+ if (!references[c.name][refId]) {
5494
+ references[c.name][refId] = [];
5495
+ }
5496
+ const referencePath = rawItem?.[REFS_PATH_FIELD];
5497
+ if (referencePath) {
5498
+ references[c.name][refId].push(referencePath);
5499
+ }
5500
+ return refId;
5435
5501
  }
5436
- }
5502
+ );
5437
5503
  return references;
5438
5504
  };
5439
5505
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
@@ -5503,7 +5569,7 @@ var Resolver = class {
5503
5569
  }
5504
5570
  break;
5505
5571
  case "rich-text":
5506
- accum[fieldName] = stringifyMDX(
5572
+ accum[fieldName] = serializeMDX(
5507
5573
  fieldValue,
5508
5574
  field,
5509
5575
  (fieldValue2) => resolveMediaCloudToRelative(
@@ -5610,8 +5676,129 @@ var resolveDateInput = (value) => {
5610
5676
  return date;
5611
5677
  };
5612
5678
 
5613
- // src/resolve.ts
5614
- import set from "lodash.set";
5679
+ // src/resolver/auth-fields.ts
5680
+ import { set } from "es-toolkit/compat";
5681
+ async function getUserDocumentContext(tinaSchema, resolver) {
5682
+ const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5683
+ if (!collection) {
5684
+ throw new Error("Auth collection not found");
5685
+ }
5686
+ const userFields = mapUserFields(collection, ["_rawData"]);
5687
+ if (!userFields.length) {
5688
+ throw new Error(`No user field found in collection ${collection.name}`);
5689
+ }
5690
+ if (userFields.length > 1) {
5691
+ throw new Error(
5692
+ `Multiple user fields found in collection ${collection.name}`
5693
+ );
5694
+ }
5695
+ const userField = userFields[0];
5696
+ const realPath = `${collection.path}/index.json`;
5697
+ const userDoc = await resolver.getDocument(realPath);
5698
+ const users = get(userDoc, userField.path);
5699
+ if (!users) {
5700
+ throw new Error("No users found");
5701
+ }
5702
+ return { collection, userField, users, userDoc, realPath };
5703
+ }
5704
+ function findUserInCollection(users, userField, userSub) {
5705
+ const { idFieldName } = userField;
5706
+ if (!idFieldName) {
5707
+ throw new Error("No uid field found on user field");
5708
+ }
5709
+ return users.find((u) => u[idFieldName] === userSub) || null;
5710
+ }
5711
+ async function handleAuthenticate({
5712
+ tinaSchema,
5713
+ resolver,
5714
+ sub,
5715
+ password,
5716
+ ctxUser
5717
+ }) {
5718
+ const userSub = sub || ctxUser?.sub;
5719
+ const { userField, users } = await getUserDocumentContext(
5720
+ tinaSchema,
5721
+ resolver
5722
+ );
5723
+ const user = findUserInCollection(users, userField, userSub);
5724
+ if (!user) {
5725
+ return null;
5726
+ }
5727
+ const { passwordFieldName } = userField;
5728
+ const saltedHash = get(user, [passwordFieldName || "", "value"]);
5729
+ if (!saltedHash) {
5730
+ throw new Error("No password field found on user field");
5731
+ }
5732
+ const matches = await checkPasswordHash({
5733
+ saltedHash,
5734
+ password
5735
+ });
5736
+ return matches ? user : null;
5737
+ }
5738
+ async function handleAuthorize({
5739
+ tinaSchema,
5740
+ resolver,
5741
+ sub,
5742
+ ctxUser
5743
+ }) {
5744
+ const userSub = sub || ctxUser?.sub;
5745
+ const { userField, users } = await getUserDocumentContext(
5746
+ tinaSchema,
5747
+ resolver
5748
+ );
5749
+ const user = findUserInCollection(users, userField, userSub);
5750
+ return user ? user : null;
5751
+ }
5752
+ async function handleUpdatePassword({
5753
+ tinaSchema,
5754
+ resolver,
5755
+ password,
5756
+ ctxUser
5757
+ }) {
5758
+ if (!ctxUser?.sub) {
5759
+ throw new Error("Not authorized");
5760
+ }
5761
+ if (!password) {
5762
+ throw new Error("No password provided");
5763
+ }
5764
+ const { collection, userField, users, realPath } = await getUserDocumentContext(tinaSchema, resolver);
5765
+ const { idFieldName, passwordFieldName } = userField;
5766
+ const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5767
+ if (!user) {
5768
+ throw new Error("Not authorized");
5769
+ }
5770
+ user[passwordFieldName] = {
5771
+ value: password,
5772
+ passwordChangeRequired: false
5773
+ };
5774
+ const params = {};
5775
+ set(
5776
+ params,
5777
+ userField.path.slice(1),
5778
+ // remove _rawData from users path
5779
+ users.map((u) => {
5780
+ if (user[idFieldName] === u[idFieldName]) {
5781
+ return user;
5782
+ }
5783
+ return {
5784
+ // don't overwrite other users' passwords
5785
+ ...u,
5786
+ [passwordFieldName]: {
5787
+ ...u[passwordFieldName],
5788
+ value: ""
5789
+ }
5790
+ };
5791
+ })
5792
+ );
5793
+ await resolver.updateResolveDocument({
5794
+ collection,
5795
+ args: { params },
5796
+ realPath,
5797
+ isCollectionSpecific: true,
5798
+ isAddPendingDocument: false
5799
+ });
5800
+ return true;
5801
+ }
5615
5802
 
5616
5803
  // src/error.ts
5617
5804
  import { GraphQLError as GraphQLError3 } from "graphql";
@@ -5721,119 +5908,33 @@ var resolve = async ({
5721
5908
  );
5722
5909
  }
5723
5910
  }
5724
- if (info.fieldName === "authenticate" || info.fieldName === "authorize") {
5725
- const sub = args.sub || ctxUser?.sub;
5726
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5727
- if (!collection) {
5728
- throw new Error("Auth collection not found");
5729
- }
5730
- const userFields = mapUserFields(collection, ["_rawData"]);
5731
- if (!userFields.length) {
5732
- throw new Error(
5733
- `No user field found in collection ${collection.name}`
5734
- );
5735
- }
5736
- if (userFields.length > 1) {
5737
- throw new Error(
5738
- `Multiple user fields found in collection ${collection.name}`
5739
- );
5740
- }
5741
- const userField = userFields[0];
5742
- const realPath = `${collection.path}/index.json`;
5743
- const userDoc = await resolver.getDocument(realPath);
5744
- const users = get(userDoc, userField.path);
5745
- if (!users) {
5746
- throw new Error("No users found");
5747
- }
5748
- const { idFieldName, passwordFieldName } = userField;
5749
- if (!idFieldName) {
5750
- throw new Error("No uid field found on user field");
5751
- }
5752
- const user = users.find((u) => u[idFieldName] === sub);
5753
- if (!user) {
5754
- return null;
5755
- }
5756
- if (info.fieldName === "authenticate") {
5757
- const saltedHash = get(user, [passwordFieldName || "", "value"]);
5758
- if (!saltedHash) {
5759
- throw new Error("No password field found on user field");
5760
- }
5761
- const matches = await checkPasswordHash({
5762
- saltedHash,
5763
- password: args.password
5764
- });
5765
- if (matches) {
5766
- return user;
5767
- }
5768
- return null;
5769
- }
5770
- return user;
5911
+ if (info.fieldName === "authenticate") {
5912
+ return handleAuthenticate({
5913
+ tinaSchema,
5914
+ resolver,
5915
+ sub: args.sub,
5916
+ password: args.password,
5917
+ info,
5918
+ ctxUser
5919
+ });
5920
+ }
5921
+ if (info.fieldName === "authorize") {
5922
+ return handleAuthorize({
5923
+ tinaSchema,
5924
+ resolver,
5925
+ sub: args.sub,
5926
+ info,
5927
+ ctxUser
5928
+ });
5771
5929
  }
5772
5930
  if (info.fieldName === "updatePassword") {
5773
- if (!ctxUser?.sub) {
5774
- throw new Error("Not authorized");
5775
- }
5776
- if (!args.password) {
5777
- throw new Error("No password provided");
5778
- }
5779
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5780
- if (!collection) {
5781
- throw new Error("Auth collection not found");
5782
- }
5783
- const userFields = mapUserFields(collection, ["_rawData"]);
5784
- if (!userFields.length) {
5785
- throw new Error(
5786
- `No user field found in collection ${collection.name}`
5787
- );
5788
- }
5789
- if (userFields.length > 1) {
5790
- throw new Error(
5791
- `Multiple user fields found in collection ${collection.name}`
5792
- );
5793
- }
5794
- const userField = userFields[0];
5795
- const realPath = `${collection.path}/index.json`;
5796
- const userDoc = await resolver.getDocument(realPath);
5797
- const users = get(userDoc, userField.path);
5798
- if (!users) {
5799
- throw new Error("No users found");
5800
- }
5801
- const { idFieldName, passwordFieldName } = userField;
5802
- const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5803
- if (!user) {
5804
- throw new Error("Not authorized");
5805
- }
5806
- user[passwordFieldName] = {
5807
- value: args.password,
5808
- passwordChangeRequired: false
5809
- };
5810
- const params = {};
5811
- set(
5812
- params,
5813
- userField.path.slice(1),
5814
- // remove _rawData from users path
5815
- users.map((u) => {
5816
- if (user[idFieldName] === u[idFieldName]) {
5817
- return user;
5818
- }
5819
- return {
5820
- // don't overwrite other users' passwords
5821
- ...u,
5822
- [passwordFieldName]: {
5823
- ...u[passwordFieldName],
5824
- value: ""
5825
- }
5826
- };
5827
- })
5828
- );
5829
- await resolver.updateResolveDocument({
5830
- collection,
5831
- args: { params },
5832
- realPath,
5833
- isCollectionSpecific: true,
5834
- isAddPendingDocument: false
5931
+ return handleUpdatePassword({
5932
+ tinaSchema,
5933
+ resolver,
5934
+ password: args.password,
5935
+ info,
5936
+ ctxUser
5835
5937
  });
5836
- return true;
5837
5938
  }
5838
5939
  if (!lookup) {
5839
5940
  return value;
@@ -6046,7 +6147,7 @@ import path4 from "node:path";
6046
6147
  import { GraphQLError as GraphQLError5 } from "graphql";
6047
6148
  import micromatch2 from "micromatch";
6048
6149
  import sha2 from "js-sha1";
6049
- import set2 from "lodash.set";
6150
+ import { set as set2 } from "es-toolkit/compat";
6050
6151
  var createLocalDatabase = (config) => {
6051
6152
  const level = new TinaLevelClient(config?.port);
6052
6153
  level.openConnection();
@@ -6177,6 +6278,7 @@ var Database = class {
6177
6278
  );
6178
6279
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
6179
6280
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6281
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6180
6282
  const normalizedPath = normalizePath(filepath);
6181
6283
  if (!collection?.isDetached) {
6182
6284
  if (this.bridge) {
@@ -6205,6 +6307,14 @@ var Database = class {
6205
6307
  let delOps = [];
6206
6308
  if (!isGitKeep(normalizedPath, collection)) {
6207
6309
  putOps = [
6310
+ ...makeRefOpsForDocument(
6311
+ normalizedPath,
6312
+ collection?.name,
6313
+ collectionReferences,
6314
+ dataFields,
6315
+ "put",
6316
+ level
6317
+ ),
6208
6318
  ...makeIndexOpsForDocument(
6209
6319
  normalizedPath,
6210
6320
  collection?.name,
@@ -6228,6 +6338,14 @@ var Database = class {
6228
6338
  SUBLEVEL_OPTIONS
6229
6339
  ).get(normalizedPath);
6230
6340
  delOps = existingItem ? [
6341
+ ...makeRefOpsForDocument(
6342
+ normalizedPath,
6343
+ collection?.name,
6344
+ collectionReferences,
6345
+ existingItem,
6346
+ "del",
6347
+ level
6348
+ ),
6231
6349
  ...makeIndexOpsForDocument(
6232
6350
  normalizedPath,
6233
6351
  collection?.name,
@@ -6275,6 +6393,7 @@ var Database = class {
6275
6393
  );
6276
6394
  collectionIndexDefinitions = indexDefinitions?.[collectionName];
6277
6395
  }
6396
+ const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
6278
6397
  const normalizedPath = normalizePath(filepath);
6279
6398
  const dataFields = await this.formatBodyOnPayload(filepath, data);
6280
6399
  const collection = await this.collectionForPath(filepath);
@@ -6322,6 +6441,14 @@ var Database = class {
6322
6441
  let delOps = [];
6323
6442
  if (!isGitKeep(normalizedPath, collection)) {
6324
6443
  putOps = [
6444
+ ...makeRefOpsForDocument(
6445
+ normalizedPath,
6446
+ collectionName,
6447
+ collectionReferences,
6448
+ dataFields,
6449
+ "put",
6450
+ level
6451
+ ),
6325
6452
  ...makeIndexOpsForDocument(
6326
6453
  normalizedPath,
6327
6454
  collectionName,
@@ -6345,6 +6472,14 @@ var Database = class {
6345
6472
  SUBLEVEL_OPTIONS
6346
6473
  ).get(normalizedPath);
6347
6474
  delOps = existingItem ? [
6475
+ ...makeRefOpsForDocument(
6476
+ normalizedPath,
6477
+ collectionName,
6478
+ collectionReferences,
6479
+ existingItem,
6480
+ "del",
6481
+ level
6482
+ ),
6348
6483
  ...makeIndexOpsForDocument(
6349
6484
  normalizedPath,
6350
6485
  collectionName,
@@ -6387,8 +6522,7 @@ var Database = class {
6387
6522
  throw new TinaFetchError(`Error in PUT for ${filepath}`, {
6388
6523
  originalError: error,
6389
6524
  file: filepath,
6390
- collection: collectionName,
6391
- stack: error.stack
6525
+ collection: collectionName
6392
6526
  });
6393
6527
  }
6394
6528
  };
@@ -6507,6 +6641,22 @@ var Database = class {
6507
6641
  this.tinaSchema = await createSchema({ schema });
6508
6642
  return this.tinaSchema;
6509
6643
  };
6644
+ this.getCollectionReferences = async (level) => {
6645
+ if (this.collectionReferences) {
6646
+ return this.collectionReferences;
6647
+ }
6648
+ const result = {};
6649
+ const schema = await this.getSchema(level || this.contentLevel);
6650
+ const collections = schema.getCollections();
6651
+ for (const collection of collections) {
6652
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6653
+ collection.name
6654
+ );
6655
+ result[collection.name] = collectionReferences;
6656
+ }
6657
+ this.collectionReferences = result;
6658
+ return result;
6659
+ };
6510
6660
  this.getIndexDefinitions = async (level) => {
6511
6661
  if (!this.collectionIndexDefinitions) {
6512
6662
  await new Promise(async (resolve2, reject) => {
@@ -6516,11 +6666,53 @@ var Database = class {
6516
6666
  const collections = schema.getCollections();
6517
6667
  for (const collection of collections) {
6518
6668
  const indexDefinitions = {
6519
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6669
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6520
6670
  // provide a default sort key which is the file sort
6671
+ // pseudo-index for the collection's references
6672
+ [REFS_COLLECTIONS_SORT_KEY]: {
6673
+ fields: [
6674
+ {
6675
+ name: REFS_REFERENCE_FIELD,
6676
+ type: "string",
6677
+ list: false
6678
+ },
6679
+ {
6680
+ name: REFS_PATH_FIELD,
6681
+ type: "string",
6682
+ list: false
6683
+ }
6684
+ ]
6685
+ }
6521
6686
  };
6522
- if (collection.fields) {
6523
- for (const field of collection.fields) {
6687
+ let fields = [];
6688
+ if (collection.templates) {
6689
+ const templateFieldMap = {};
6690
+ const conflictedFields = /* @__PURE__ */ new Set();
6691
+ for (const template of collection.templates) {
6692
+ for (const field of template.fields) {
6693
+ if (!templateFieldMap[field.name]) {
6694
+ templateFieldMap[field.name] = field;
6695
+ } else {
6696
+ if (templateFieldMap[field.name].type !== field.type) {
6697
+ console.warn(
6698
+ `Field ${field.name} has conflicting types in templates - skipping index`
6699
+ );
6700
+ conflictedFields.add(field.name);
6701
+ }
6702
+ }
6703
+ }
6704
+ }
6705
+ for (const conflictedField in conflictedFields) {
6706
+ delete templateFieldMap[conflictedField];
6707
+ }
6708
+ for (const field of Object.values(templateFieldMap)) {
6709
+ fields.push(field);
6710
+ }
6711
+ } else if (collection.fields) {
6712
+ fields = collection.fields;
6713
+ }
6714
+ if (fields) {
6715
+ for (const field of fields) {
6524
6716
  if (field.indexed !== void 0 && field.indexed === false || field.type === "object") {
6525
6717
  continue;
6526
6718
  }
@@ -6668,29 +6860,35 @@ var Database = class {
6668
6860
  }
6669
6861
  startKey = startKey || key || "";
6670
6862
  endKey = key || "";
6671
- edges = [...edges, { cursor: key, path: filepath }];
6863
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6672
6864
  }
6673
6865
  return {
6674
- edges: await sequential(edges, async (edge) => {
6675
- try {
6676
- const node = await hydrator(edge.path);
6677
- return {
6678
- node,
6679
- cursor: btoa(edge.cursor)
6680
- };
6681
- } catch (error) {
6682
- console.log(error);
6683
- if (error instanceof Error && (!edge.path.includes(".tina/__generated__/_graphql.json") || !edge.path.includes("tina/__generated__/_graphql.json"))) {
6684
- throw new TinaQueryError({
6685
- originalError: error,
6686
- file: edge.path,
6687
- collection: collection.name,
6688
- stack: error.stack
6689
- });
6866
+ edges: await sequential(
6867
+ edges,
6868
+ async ({
6869
+ cursor,
6870
+ path: path7,
6871
+ value
6872
+ }) => {
6873
+ try {
6874
+ const node = await hydrator(path7, value);
6875
+ return {
6876
+ node,
6877
+ cursor: btoa(cursor)
6878
+ };
6879
+ } catch (error) {
6880
+ console.log(error);
6881
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6882
+ throw new TinaQueryError({
6883
+ originalError: error,
6884
+ file: path7,
6885
+ collection: collection.name
6886
+ });
6887
+ }
6888
+ throw error;
6690
6889
  }
6691
- throw error;
6692
6890
  }
6693
- }),
6891
+ ),
6694
6892
  pageInfo: {
6695
6893
  hasPreviousPage,
6696
6894
  hasNextPage,
@@ -6814,13 +7012,14 @@ var Database = class {
6814
7012
  documentPaths,
6815
7013
  async (collection, documentPaths2) => {
6816
7014
  if (collection && !collection.isDetached) {
6817
- await _indexContent(
6818
- this,
6819
- this.contentLevel,
6820
- documentPaths2,
7015
+ await _indexContent({
7016
+ database: this,
7017
+ level: this.contentLevel,
7018
+ documentPaths: documentPaths2,
6821
7019
  enqueueOps,
6822
- collection
6823
- );
7020
+ collection,
7021
+ isPartialReindex: true
7022
+ });
6824
7023
  }
6825
7024
  }
6826
7025
  );
@@ -6836,6 +7035,7 @@ var Database = class {
6836
7035
  throw new Error(`No collection found for path: ${filepath}`);
6837
7036
  }
6838
7037
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7038
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6839
7039
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6840
7040
  let level = this.contentLevel;
6841
7041
  if (collection?.isDetached) {
@@ -6854,6 +7054,14 @@ var Database = class {
6854
7054
  collection.path || ""
6855
7055
  );
6856
7056
  await this.contentLevel.batch([
7057
+ ...makeRefOpsForDocument(
7058
+ normalizedPath,
7059
+ collection.name,
7060
+ collectionReferences,
7061
+ item,
7062
+ "del",
7063
+ level
7064
+ ),
6857
7065
  ...makeIndexOpsForDocument(
6858
7066
  normalizedPath,
6859
7067
  collection.name,
@@ -6918,20 +7126,26 @@ var Database = class {
6918
7126
  );
6919
7127
  const doc = await level2.keys({ limit: 1 }).next();
6920
7128
  if (!doc) {
6921
- await _indexContent(
6922
- this,
6923
- level2,
6924
- contentPaths,
7129
+ await _indexContent({
7130
+ database: this,
7131
+ level: level2,
7132
+ documentPaths: contentPaths,
6925
7133
  enqueueOps,
6926
7134
  collection,
6927
- userFields.map((field) => [
7135
+ passwordFields: userFields.map((field) => [
6928
7136
  ...field.path,
6929
7137
  field.passwordFieldName
6930
7138
  ])
6931
- );
7139
+ });
6932
7140
  }
6933
7141
  } else {
6934
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7142
+ await _indexContent({
7143
+ database: this,
7144
+ level,
7145
+ documentPaths: contentPaths,
7146
+ enqueueOps,
7147
+ collection
7148
+ });
6935
7149
  }
6936
7150
  }
6937
7151
  );
@@ -7070,7 +7284,15 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
7070
7284
  )
7071
7285
  );
7072
7286
  var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${collection?.format || "md"}`);
7073
- var _indexContent = async (database, level, documentPaths, enqueueOps, collection, passwordFields) => {
7287
+ var _indexContent = async ({
7288
+ database,
7289
+ level,
7290
+ documentPaths,
7291
+ enqueueOps,
7292
+ collection,
7293
+ passwordFields,
7294
+ isPartialReindex
7295
+ }) => {
7074
7296
  let collectionIndexDefinitions;
7075
7297
  let collectionPath;
7076
7298
  if (collection) {
@@ -7081,6 +7303,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7081
7303
  }
7082
7304
  collectionPath = collection.path;
7083
7305
  }
7306
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
7084
7307
  const tinaSchema = await database.getSchema();
7085
7308
  let templateInfo = null;
7086
7309
  if (collection) {
@@ -7102,12 +7325,61 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7102
7325
  await hashPasswordValues(aliasedData, passwordFields);
7103
7326
  }
7104
7327
  const normalizedPath = normalizePath(filepath);
7328
+ const rootSublevel = level.sublevel(
7329
+ CONTENT_ROOT_PREFIX,
7330
+ SUBLEVEL_OPTIONS
7331
+ );
7105
7332
  const folderKey = folderTreeBuilder.update(
7106
7333
  normalizedPath,
7107
7334
  collectionPath || ""
7108
7335
  );
7336
+ if (isPartialReindex) {
7337
+ const item = await rootSublevel.get(normalizedPath);
7338
+ if (item) {
7339
+ await database.contentLevel.batch([
7340
+ ...makeRefOpsForDocument(
7341
+ normalizedPath,
7342
+ collection?.name,
7343
+ collectionReferences,
7344
+ item,
7345
+ "del",
7346
+ level
7347
+ ),
7348
+ ...makeIndexOpsForDocument(
7349
+ normalizedPath,
7350
+ collection.name,
7351
+ collectionIndexDefinitions,
7352
+ item,
7353
+ "del",
7354
+ level
7355
+ ),
7356
+ // folder indices
7357
+ ...makeIndexOpsForDocument(
7358
+ normalizedPath,
7359
+ `${collection.name}_${folderKey}`,
7360
+ collectionIndexDefinitions,
7361
+ item,
7362
+ "del",
7363
+ level
7364
+ ),
7365
+ {
7366
+ type: "del",
7367
+ key: normalizedPath,
7368
+ sublevel: rootSublevel
7369
+ }
7370
+ ]);
7371
+ }
7372
+ }
7109
7373
  if (!isGitKeep(filepath, collection)) {
7110
7374
  await enqueueOps([
7375
+ ...makeRefOpsForDocument(
7376
+ normalizedPath,
7377
+ collection?.name,
7378
+ collectionReferences,
7379
+ aliasedData,
7380
+ "put",
7381
+ level
7382
+ ),
7111
7383
  ...makeIndexOpsForDocument(
7112
7384
  normalizedPath,
7113
7385
  collection?.name,
@@ -7140,8 +7412,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7140
7412
  throw new TinaFetchError(`Unable to seed ${filepath}`, {
7141
7413
  originalError: error,
7142
7414
  file: filepath,
7143
- collection: collection?.name,
7144
- stack: error.stack
7415
+ collection: collection?.name
7145
7416
  });
7146
7417
  }
7147
7418
  });
@@ -7171,6 +7442,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7171
7442
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
7172
7443
  }
7173
7444
  }
7445
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
7174
7446
  const tinaSchema = await database.getSchema();
7175
7447
  let templateInfo = null;
7176
7448
  if (collection) {
@@ -7194,6 +7466,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7194
7466
  item
7195
7467
  ) : item;
7196
7468
  await enqueueOps([
7469
+ ...makeRefOpsForDocument(
7470
+ itemKey,
7471
+ collection?.name,
7472
+ collectionReferences,
7473
+ aliasedData,
7474
+ "del",
7475
+ database.contentLevel
7476
+ ),
7197
7477
  ...makeIndexOpsForDocument(
7198
7478
  itemKey,
7199
7479
  collection.name,
@@ -7323,8 +7603,8 @@ import path6 from "path";
7323
7603
  import normalize from "normalize-path";
7324
7604
  var FilesystemBridge = class {
7325
7605
  constructor(rootPath, outputPath) {
7326
- this.rootPath = rootPath || "";
7327
- this.outputPath = outputPath || rootPath;
7606
+ this.rootPath = path6.resolve(rootPath);
7607
+ this.outputPath = outputPath ? path6.resolve(outputPath) : this.rootPath;
7328
7608
  }
7329
7609
  async glob(pattern, extension) {
7330
7610
  const basePath = path6.join(this.outputPath, ...pattern.split("/"));
@@ -7336,19 +7616,19 @@ var FilesystemBridge = class {
7336
7616
  }
7337
7617
  );
7338
7618
  const posixRootPath = normalize(this.outputPath);
7339
- return items.map((item) => {
7340
- return item.replace(posixRootPath, "").replace(/^\/|\/$/g, "");
7341
- });
7619
+ return items.map(
7620
+ (item) => item.substring(posixRootPath.length).replace(/^\/|\/$/g, "")
7621
+ );
7342
7622
  }
7343
7623
  async delete(filepath) {
7344
7624
  await fs2.remove(path6.join(this.outputPath, filepath));
7345
7625
  }
7346
7626
  async get(filepath) {
7347
- return fs2.readFileSync(path6.join(this.outputPath, filepath)).toString();
7627
+ return (await fs2.readFile(path6.join(this.outputPath, filepath))).toString();
7348
7628
  }
7349
7629
  async put(filepath, data, basePathOverride) {
7350
7630
  const basePath = basePathOverride || this.outputPath;
7351
- await fs2.outputFileSync(path6.join(basePath, filepath), data);
7631
+ await fs2.outputFile(path6.join(basePath, filepath), data);
7352
7632
  }
7353
7633
  };
7354
7634
  var AuditFileSystemBridge = class extends FilesystemBridge {