@tinacms/graphql 0.0.0-d7c745e-20250102002342 → 0.0.0-d9487bf-20251119052214

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
@@ -1029,41 +1029,6 @@ function* walk(maybeNode, visited = /* @__PURE__ */ new WeakSet()) {
1029
1029
  yield maybeNode;
1030
1030
  visited.add(maybeNode);
1031
1031
  }
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
1032
  var generateNamespacedFieldName = (names, suffix = "") => {
1068
1033
  return (suffix ? [...names, suffix] : names).map(capitalize).join("");
1069
1034
  };
@@ -1882,7 +1847,7 @@ var Builder = class {
1882
1847
  * ```
1883
1848
  *
1884
1849
  * @public
1885
- * @param collection a Tina Cloud collection
1850
+ * @param collection a TinaCloud collection
1886
1851
  */
1887
1852
  this.collectionFragment = async (collection) => {
1888
1853
  const name = NAMER.dataTypeName(collection.namespace);
@@ -2746,7 +2711,7 @@ var Builder = class {
2746
2711
  this._buildDataField = async (field) => {
2747
2712
  const listWarningMsg = `
2748
2713
  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
2714
+ Visit https://tina.io/docs/r/content-fields/#list-fields/ for more information
2750
2715
 
2751
2716
  `;
2752
2717
  switch (field.type) {
@@ -2898,6 +2863,7 @@ var filterSelections = (arr) => {
2898
2863
  import { TinaSchema } from "@tinacms/schema-tools";
2899
2864
 
2900
2865
  // src/schema/validate.ts
2866
+ import { addNamespaceToSchema } from "@tinacms/schema-tools";
2901
2867
  import deepClone from "lodash.clonedeep";
2902
2868
  import * as yup2 from "yup";
2903
2869
  import {
@@ -3053,7 +3019,7 @@ var validateField = async (field) => {
3053
3019
  // package.json
3054
3020
  var package_default = {
3055
3021
  name: "@tinacms/graphql",
3056
- version: "1.5.9",
3022
+ version: "1.6.1",
3057
3023
  main: "dist/index.js",
3058
3024
  module: "dist/index.mjs",
3059
3025
  typings: "dist/index.d.ts",
@@ -3079,33 +3045,32 @@ var package_default = {
3079
3045
  types: "pnpm tsc",
3080
3046
  build: "tinacms-scripts build",
3081
3047
  docs: "pnpm typedoc",
3082
- serve: "pnpm nodemon dist/server.js",
3083
- test: "jest",
3084
- "test-watch": "jest --watch"
3048
+ test: "vitest run",
3049
+ "test-watch": "vitest"
3085
3050
  },
3086
3051
  dependencies: {
3087
- "@iarna/toml": "^2.2.5",
3052
+ "@iarna/toml": "catalog:",
3088
3053
  "@tinacms/mdx": "workspace:*",
3089
3054
  "@tinacms/schema-tools": "workspace:*",
3090
- "abstract-level": "^1.0.4",
3055
+ "abstract-level": "catalog:",
3091
3056
  "date-fns": "^2.30.0",
3092
- "fast-glob": "^3.3.2",
3093
- "fs-extra": "^11.2.0",
3094
- "glob-parent": "^6.0.2",
3057
+ "fast-glob": "catalog:",
3058
+ "fs-extra": "catalog:",
3059
+ "glob-parent": "catalog:",
3095
3060
  graphql: "15.8.0",
3096
- "gray-matter": "^4.0.3",
3097
- "isomorphic-git": "^1.27.1",
3098
- "js-sha1": "^0.6.0",
3061
+ "gray-matter": "catalog:",
3062
+ "isomorphic-git": "catalog:",
3063
+ "js-sha1": "catalog:",
3099
3064
  "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",
3065
+ "jsonpath-plus": "catalog:",
3066
+ "lodash.clonedeep": "catalog:",
3067
+ "lodash.set": "catalog:",
3068
+ "lodash.uniqby": "catalog:",
3069
+ "many-level": "catalog:",
3070
+ micromatch: "catalog:",
3071
+ "normalize-path": "catalog:",
3072
+ "readable-stream": "catalog:",
3073
+ scmp: "catalog:",
3109
3074
  yup: "^0.32.11"
3110
3075
  },
3111
3076
  publishConfig: {
@@ -3120,26 +3085,24 @@ var package_default = {
3120
3085
  "@tinacms/scripts": "workspace:*",
3121
3086
  "@types/cors": "^2.8.17",
3122
3087
  "@types/estree": "^0.0.50",
3123
- "@types/express": "^4.17.21",
3088
+ "@types/express": "catalog:",
3124
3089
  "@types/fs-extra": "^9.0.13",
3125
- "@types/jest": "^26.0.24",
3126
3090
  "@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",
3091
+ "@types/lodash.camelcase": "catalog:",
3092
+ "@types/lodash.upperfirst": "catalog:",
3093
+ "@types/lru-cache": "catalog:",
3094
+ "@types/mdast": "catalog:",
3095
+ "@types/micromatch": "catalog:",
3096
+ "@types/node": "^22.13.1",
3097
+ "@types/normalize-path": "catalog:",
3098
+ "@types/ws": "catalog:",
3135
3099
  "@types/yup": "^0.29.14",
3136
- jest: "^29.7.0",
3137
- "jest-diff": "^29.7.0",
3138
3100
  "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"
3101
+ "memory-level": "catalog:",
3102
+ typescript: "^5.7.3",
3103
+ vite: "^4.5.9",
3104
+ vitest: "^0.32.4",
3105
+ zod: "catalog:"
3143
3106
  }
3144
3107
  };
3145
3108
 
@@ -3295,7 +3258,9 @@ var _buildSchema = async (builder, tinaSchema) => {
3295
3258
  await builder.buildCreateCollectionFolderMutation()
3296
3259
  );
3297
3260
  await sequential(collections, async (collection) => {
3298
- queryTypeDefinitionFields.push(await builder.collectionDocument(collection));
3261
+ queryTypeDefinitionFields.push(
3262
+ await builder.collectionDocument(collection)
3263
+ );
3299
3264
  if (collection.isAuthCollection) {
3300
3265
  queryTypeDefinitionFields.push(
3301
3266
  await builder.authenticationCollectionDocument(collection)
@@ -3345,249 +3310,10 @@ import { graphql, buildASTSchema, getNamedType, GraphQLError as GraphQLError4 }
3345
3310
  // src/resolver/index.ts
3346
3311
  import path3 from "path";
3347
3312
  import isValid from "date-fns/isValid/index.js";
3348
-
3349
- // src/mdx/index.ts
3350
- import { parseMDX, stringifyMDX } from "@tinacms/mdx";
3351
-
3352
- // src/resolver/index.ts
3353
3313
  import { JSONPath as JSONPath2 } from "jsonpath-plus";
3354
3314
 
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
- }
3364
- };
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;
3373
- }
3374
- };
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
- );
3381
- }
3382
- };
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
3388
- );
3389
- }
3390
- toString() {
3391
- return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
3392
- }
3393
- };
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);
3402
- }
3403
- }
3404
- } else {
3405
- console.error(e);
3406
- }
3407
- throw e;
3408
- };
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
3415
- );
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
- }
3455
- }
3456
- } else {
3457
- throw new Error(`Unable to find field ${fieldKey}`);
3458
- }
3459
- }
3460
- };
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
3473
- );
3474
- }
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
- };
3315
+ // src/mdx/index.ts
3316
+ import { parseMDX, serializeMDX } from "@tinacms/mdx";
3591
3317
 
3592
3318
  // src/resolver/index.ts
3593
3319
  import { GraphQLError as GraphQLError2 } from "graphql";
@@ -3610,7 +3336,9 @@ var LevelProxyHandler = {
3610
3336
  throw new Error(`The property, ${property.toString()}, doesn't exist`);
3611
3337
  }
3612
3338
  if (typeof target[property] !== "function") {
3613
- throw new Error(`The property, ${property.toString()}, is not a function`);
3339
+ throw new Error(
3340
+ `The property, ${property.toString()}, is not a function`
3341
+ );
3614
3342
  }
3615
3343
  if (property === "get") {
3616
3344
  return async (...args) => {
@@ -3648,13 +3376,13 @@ import path2 from "path";
3648
3376
 
3649
3377
  // src/database/util.ts
3650
3378
  import toml from "@iarna/toml";
3651
- import yaml from "js-yaml";
3652
- import matter from "gray-matter";
3653
3379
  import {
3654
3380
  normalizePath
3655
3381
  } from "@tinacms/schema-tools";
3656
- import micromatch from "micromatch";
3382
+ import matter from "gray-matter";
3383
+ import yaml from "js-yaml";
3657
3384
  import path from "path";
3385
+ import micromatch from "micromatch";
3658
3386
 
3659
3387
  // src/database/alias-utils.ts
3660
3388
  var replaceBlockAliases = (template, item) => {
@@ -3995,6 +3723,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3995
3723
 
3996
3724
  // src/database/datalayer.ts
3997
3725
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
3726
+ var REFS_COLLECTIONS_SORT_KEY = "__refs__";
3727
+ var REFS_REFERENCE_FIELD = "__tina_ref__";
3728
+ var REFS_PATH_FIELD = "__tina_ref_path__";
3998
3729
  var DEFAULT_NUMERIC_LPAD = 4;
3999
3730
  var applyPadding = (input, pad) => {
4000
3731
  if (pad) {
@@ -4568,6 +4299,57 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4568
4299
  }
4569
4300
  return result;
4570
4301
  };
4302
+ var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4303
+ const result = [];
4304
+ if (collection) {
4305
+ for (const [c, referencePaths] of Object.entries(references || {})) {
4306
+ if (!referencePaths.length) {
4307
+ continue;
4308
+ }
4309
+ const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4310
+ const refSublevel = collectionSublevel.sublevel(
4311
+ REFS_COLLECTIONS_SORT_KEY,
4312
+ SUBLEVEL_OPTIONS
4313
+ );
4314
+ const references2 = {};
4315
+ for (const path7 of referencePaths) {
4316
+ const ref = JSONPath({ path: path7, json: data });
4317
+ if (!ref) {
4318
+ continue;
4319
+ }
4320
+ if (Array.isArray(ref)) {
4321
+ for (const r of ref) {
4322
+ if (!r) {
4323
+ continue;
4324
+ }
4325
+ if (references2[r]) {
4326
+ references2[r].push(path7);
4327
+ } else {
4328
+ references2[r] = [path7];
4329
+ }
4330
+ }
4331
+ } else {
4332
+ if (references2[ref]) {
4333
+ references2[ref].push(path7);
4334
+ } else {
4335
+ references2[ref] = [path7];
4336
+ }
4337
+ }
4338
+ }
4339
+ for (const ref of Object.keys(references2)) {
4340
+ for (const path7 of references2[ref]) {
4341
+ result.push({
4342
+ type: opType,
4343
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path7}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4344
+ sublevel: refSublevel,
4345
+ value: opType === "put" ? {} : void 0
4346
+ });
4347
+ }
4348
+ }
4349
+ }
4350
+ }
4351
+ return result;
4352
+ };
4571
4353
  var makeStringEscaper = (regex, replacement) => {
4572
4354
  return (input) => {
4573
4355
  if (Array.isArray(input)) {
@@ -4581,12 +4363,248 @@ var makeStringEscaper = (regex, replacement) => {
4581
4363
  return input;
4582
4364
  }
4583
4365
  }
4584
- };
4366
+ };
4367
+ };
4368
+ var stringEscaper = makeStringEscaper(
4369
+ new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4370
+ encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4371
+ );
4372
+
4373
+ // src/resolver/error.ts
4374
+ var TinaGraphQLError = class extends Error {
4375
+ constructor(message, extensions) {
4376
+ super(message);
4377
+ if (!this.name) {
4378
+ Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
4379
+ }
4380
+ this.extensions = { ...extensions };
4381
+ }
4382
+ };
4383
+ var TinaFetchError = class extends Error {
4384
+ constructor(message, args) {
4385
+ super(message);
4386
+ this.name = "TinaFetchError";
4387
+ this.collection = args.collection;
4388
+ this.file = args.file;
4389
+ this.originalError = args.originalError;
4390
+ }
4391
+ };
4392
+ var TinaQueryError = class extends TinaFetchError {
4393
+ constructor(args) {
4394
+ super(
4395
+ `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4396
+ args
4397
+ );
4398
+ }
4399
+ };
4400
+ var TinaParseDocumentError = class extends TinaFetchError {
4401
+ constructor(args) {
4402
+ super(
4403
+ `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
4404
+ args
4405
+ );
4406
+ }
4407
+ toString() {
4408
+ return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
4409
+ }
4410
+ };
4411
+ var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
4412
+ var handleFetchErrorError = (e, verbose) => {
4413
+ if (e instanceof Error) {
4414
+ if (e instanceof TinaFetchError) {
4415
+ if (verbose) {
4416
+ console.log(e.toString());
4417
+ console.log(e);
4418
+ console.log(e.stack);
4419
+ }
4420
+ }
4421
+ } else {
4422
+ console.error(e);
4423
+ }
4424
+ throw e;
4425
+ };
4426
+
4427
+ // src/resolver/filter-utils.ts
4428
+ var resolveReferences = async (filter, fields, resolver) => {
4429
+ for (const fieldKey of Object.keys(filter)) {
4430
+ const fieldDefinition = fields.find(
4431
+ (f) => f.name === fieldKey
4432
+ );
4433
+ if (fieldDefinition) {
4434
+ if (fieldDefinition.type === "reference") {
4435
+ const { edges, values } = await resolver(filter, fieldDefinition);
4436
+ if (edges.length === 1) {
4437
+ filter[fieldKey] = {
4438
+ eq: values[0]
4439
+ };
4440
+ } else if (edges.length > 1) {
4441
+ filter[fieldKey] = {
4442
+ in: values
4443
+ };
4444
+ } else {
4445
+ filter[fieldKey] = {
4446
+ eq: "___null___"
4447
+ };
4448
+ }
4449
+ } else if (fieldDefinition.type === "object") {
4450
+ if (fieldDefinition.templates) {
4451
+ for (const templateName of Object.keys(filter[fieldKey])) {
4452
+ const template = fieldDefinition.templates.find(
4453
+ (template2) => !(typeof template2 === "string") && template2.name === templateName
4454
+ );
4455
+ if (template) {
4456
+ await resolveReferences(
4457
+ filter[fieldKey][templateName],
4458
+ template.fields,
4459
+ resolver
4460
+ );
4461
+ } else {
4462
+ throw new Error(`Template ${templateName} not found`);
4463
+ }
4464
+ }
4465
+ } else {
4466
+ await resolveReferences(
4467
+ filter[fieldKey],
4468
+ fieldDefinition.fields,
4469
+ resolver
4470
+ );
4471
+ }
4472
+ }
4473
+ } else {
4474
+ throw new Error(`Unable to find field ${fieldKey}`);
4475
+ }
4476
+ }
4477
+ };
4478
+ var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
4479
+ for (const childFieldName of Object.keys(filterNode)) {
4480
+ const childField = fields.find((field) => field.name === childFieldName);
4481
+ if (!childField) {
4482
+ throw new Error(`Unable to find type for field ${childFieldName}`);
4483
+ }
4484
+ collectConditionsForField(
4485
+ childFieldName,
4486
+ childField,
4487
+ filterNode[childFieldName],
4488
+ pathExpression,
4489
+ collectCondition
4490
+ );
4491
+ }
4492
+ };
4493
+ var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4494
+ if (field.list && field.templates) {
4495
+ for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
4496
+ const template = field.templates.find(
4497
+ (template2) => !(typeof template2 === "string") && template2.name === filterKey
4498
+ );
4499
+ const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
4500
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
4501
+ collectConditionsForChildFields(
4502
+ childFilterNode,
4503
+ template.fields,
4504
+ filterPath,
4505
+ collectCondition
4506
+ );
4507
+ }
4508
+ } else {
4509
+ const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
4510
+ const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
4511
+ collectConditionsForChildFields(
4512
+ filterNode,
4513
+ field.fields,
4514
+ filterPath,
4515
+ collectCondition
4516
+ );
4517
+ }
4518
+ };
4519
+ var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
4520
+ if (field.type === "object") {
4521
+ collectConditionsForObjectField(
4522
+ fieldName,
4523
+ field,
4524
+ filterNode,
4525
+ pathExpression,
4526
+ collectCondition
4527
+ );
4528
+ } else {
4529
+ collectCondition({
4530
+ filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
4531
+ filterExpression: {
4532
+ _type: field.type,
4533
+ _list: !!field.list,
4534
+ ...filterNode
4535
+ }
4536
+ });
4537
+ }
4538
+ };
4539
+
4540
+ // src/resolver/media-utils.ts
4541
+ var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
4542
+ if (config && value) {
4543
+ if (config.useRelativeMedia === true) {
4544
+ return value;
4545
+ }
4546
+ if (hasTinaMediaConfig(schema) === true) {
4547
+ const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
4548
+ if (typeof value === "string" && value.includes(assetsURL)) {
4549
+ const cleanMediaRoot = cleanUpSlashes(
4550
+ schema.config.media.tina.mediaRoot
4551
+ );
4552
+ const strippedURL = value.replace(assetsURL, "");
4553
+ return `${cleanMediaRoot}${strippedURL}`;
4554
+ }
4555
+ if (Array.isArray(value)) {
4556
+ return value.map((v) => {
4557
+ if (!v || typeof v !== "string") return v;
4558
+ const cleanMediaRoot = cleanUpSlashes(
4559
+ schema.config.media.tina.mediaRoot
4560
+ );
4561
+ const strippedURL = v.replace(assetsURL, "");
4562
+ return `${cleanMediaRoot}${strippedURL}`;
4563
+ });
4564
+ }
4565
+ return value;
4566
+ }
4567
+ return value;
4568
+ } else {
4569
+ return value;
4570
+ }
4571
+ };
4572
+ var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
4573
+ if (config && value) {
4574
+ if (config.useRelativeMedia === true) {
4575
+ return value;
4576
+ }
4577
+ if (hasTinaMediaConfig(schema) === true) {
4578
+ const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
4579
+ if (typeof value === "string") {
4580
+ const strippedValue = value.replace(cleanMediaRoot, "");
4581
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4582
+ }
4583
+ if (Array.isArray(value)) {
4584
+ return value.map((v) => {
4585
+ if (!v || typeof v !== "string") return v;
4586
+ const strippedValue = v.replace(cleanMediaRoot, "");
4587
+ return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
4588
+ });
4589
+ }
4590
+ }
4591
+ return value;
4592
+ } else {
4593
+ return value;
4594
+ }
4595
+ };
4596
+ var cleanUpSlashes = (path7) => {
4597
+ if (path7) {
4598
+ return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
4599
+ }
4600
+ return "";
4601
+ };
4602
+ var hasTinaMediaConfig = (schema) => {
4603
+ if (!schema.config?.media?.tina) return false;
4604
+ if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
4605
+ return false;
4606
+ return true;
4585
4607
  };
4586
- var stringEscaper = makeStringEscaper(
4587
- new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4588
- encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4589
- );
4590
4608
 
4591
4609
  // src/resolver/index.ts
4592
4610
  var createResolver = (args) => {
@@ -4748,8 +4766,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4748
4766
  originalError: e,
4749
4767
  collection: collection.name,
4750
4768
  includeAuditMessage: !isAudit,
4751
- file: relativePath,
4752
- stack: e.stack
4769
+ file: relativePath
4753
4770
  });
4754
4771
  }
4755
4772
  const titleField = template.fields.find((x) => {
@@ -4788,24 +4805,33 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4788
4805
  throw e;
4789
4806
  }
4790
4807
  };
4791
- var updateObjectWithJsonPath = (obj, path7, newValue) => {
4808
+ var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4809
+ let updated = false;
4792
4810
  if (!path7.includes(".") && !path7.includes("[")) {
4793
- if (path7 in obj) {
4811
+ if (path7 in obj && obj[path7] === oldValue) {
4794
4812
  obj[path7] = newValue;
4813
+ updated = true;
4795
4814
  }
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" });
4815
+ return { object: obj, updated };
4816
+ }
4817
+ const parentPath = path7.replace(/\.[^.\[\]]+$/, "");
4818
+ const keyToUpdate = path7.match(/[^.\[\]]+$/)[0];
4819
+ const parents = JSONPath2({
4820
+ path: parentPath,
4821
+ json: obj,
4822
+ resultType: "value"
4823
+ });
4801
4824
  if (parents.length > 0) {
4802
4825
  parents.forEach((parent) => {
4803
4826
  if (parent && typeof parent === "object" && keyToUpdate in parent) {
4804
- parent[keyToUpdate] = newValue;
4827
+ if (parent[keyToUpdate] === oldValue) {
4828
+ parent[keyToUpdate] = newValue;
4829
+ updated = true;
4830
+ }
4805
4831
  }
4806
4832
  });
4807
4833
  }
4808
- return obj;
4834
+ return { object: obj, updated };
4809
4835
  };
4810
4836
  var Resolver = class {
4811
4837
  constructor(init) {
@@ -4822,7 +4848,9 @@ var Resolver = class {
4822
4848
  };
4823
4849
  this.getRaw = async (fullPath) => {
4824
4850
  if (typeof fullPath !== "string") {
4825
- throw new Error(`fullPath must be of type string for getDocument request`);
4851
+ throw new Error(
4852
+ `fullPath must be of type string for getDocument request`
4853
+ );
4826
4854
  }
4827
4855
  return this.database.get(fullPath);
4828
4856
  };
@@ -4851,7 +4879,9 @@ var Resolver = class {
4851
4879
  };
4852
4880
  this.getDocument = async (fullPath, opts = {}) => {
4853
4881
  if (typeof fullPath !== "string") {
4854
- throw new Error(`fullPath must be of type string for getDocument request`);
4882
+ throw new Error(
4883
+ `fullPath must be of type string for getDocument request`
4884
+ );
4855
4885
  }
4856
4886
  const rawData = await this.getRaw(fullPath);
4857
4887
  const hasReferences = opts?.checkReferences ? await this.hasReferences(fullPath, opts.collection) : void 0;
@@ -4866,7 +4896,9 @@ var Resolver = class {
4866
4896
  };
4867
4897
  this.deleteDocument = async (fullPath) => {
4868
4898
  if (typeof fullPath !== "string") {
4869
- throw new Error(`fullPath must be of type string for getDocument request`);
4899
+ throw new Error(
4900
+ `fullPath must be of type string for getDocument request`
4901
+ );
4870
4902
  }
4871
4903
  await this.database.delete(fullPath);
4872
4904
  };
@@ -5070,7 +5102,11 @@ var Resolver = class {
5070
5102
  collection,
5071
5103
  doc?._rawData
5072
5104
  );
5073
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
5105
+ await this.database.put(
5106
+ realPath,
5107
+ { ...oldDoc, ...params },
5108
+ collection.name
5109
+ );
5074
5110
  return this.getDocument(realPath);
5075
5111
  };
5076
5112
  /**
@@ -5183,17 +5219,35 @@ var Resolver = class {
5183
5219
  await this.deleteDocument(realPath);
5184
5220
  if (await this.hasReferences(realPath, collection)) {
5185
5221
  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(
5222
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5223
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5224
+ docsWithRefs
5225
+ )) {
5226
+ let refDoc = await this.getRaw(pathToDocWithRef);
5227
+ let hasUpdate = false;
5228
+ for (const path7 of referencePaths) {
5229
+ const { object: object2, updated } = updateObjectWithJsonPath(
5191
5230
  refDoc,
5192
- ref.path.join("."),
5231
+ path7,
5232
+ realPath,
5193
5233
  null
5194
5234
  );
5235
+ refDoc = object2;
5236
+ hasUpdate = updated || hasUpdate;
5237
+ }
5238
+ if (hasUpdate) {
5239
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5240
+ if (!collectionWithRef) {
5241
+ throw new Error(
5242
+ `Unable to find collection for ${pathToDocWithRef}`
5243
+ );
5244
+ }
5245
+ await this.database.put(
5246
+ pathToDocWithRef,
5247
+ refDoc,
5248
+ collectionWithRef.name
5249
+ );
5195
5250
  }
5196
- await this.database.put(refPath, refDoc, collection2);
5197
5251
  }
5198
5252
  }
5199
5253
  }
@@ -5213,26 +5267,49 @@ var Resolver = class {
5213
5267
  collection?.path,
5214
5268
  args.params.relativePath
5215
5269
  );
5270
+ if (newRealPath === realPath) {
5271
+ return doc;
5272
+ }
5216
5273
  await this.database.put(newRealPath, doc._rawData, collection.name);
5217
5274
  await this.deleteDocument(realPath);
5218
5275
  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("."),
5276
+ for (const [collection2, docsWithRefs] of Object.entries(collRefs)) {
5277
+ for (const [pathToDocWithRef, referencePaths] of Object.entries(
5278
+ docsWithRefs
5279
+ )) {
5280
+ let docWithRef = await this.getRaw(pathToDocWithRef);
5281
+ let hasUpdate = false;
5282
+ for (const path7 of referencePaths) {
5283
+ const { object: object2, updated } = updateObjectWithJsonPath(
5284
+ docWithRef,
5285
+ path7,
5286
+ realPath,
5226
5287
  newRealPath
5227
5288
  );
5289
+ docWithRef = object2;
5290
+ hasUpdate = updated || hasUpdate;
5291
+ }
5292
+ if (hasUpdate) {
5293
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5294
+ if (!collectionWithRef) {
5295
+ throw new Error(
5296
+ `Unable to find collection for ${pathToDocWithRef}`
5297
+ );
5298
+ }
5299
+ await this.database.put(
5300
+ pathToDocWithRef,
5301
+ docWithRef,
5302
+ collectionWithRef.name
5303
+ );
5228
5304
  }
5229
- await this.database.put(refPath, refDoc, collection2);
5230
5305
  }
5231
5306
  }
5232
5307
  return this.getDocument(newRealPath);
5233
5308
  }
5234
5309
  if (alreadyExists === false) {
5235
- throw new Error(`Unable to update document, ${realPath} does not exist`);
5310
+ throw new Error(
5311
+ `Unable to update document, ${realPath} does not exist`
5312
+ );
5236
5313
  }
5237
5314
  return this.updateResolveDocument({
5238
5315
  collection,
@@ -5358,35 +5435,30 @@ var Resolver = class {
5358
5435
  */
5359
5436
  this.hasReferences = async (id, c) => {
5360
5437
  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
- }
5438
+ await this.database.query(
5439
+ {
5440
+ collection: c.name,
5441
+ filterChain: makeFilterChain({
5442
+ conditions: [
5443
+ {
5444
+ filterPath: REFS_REFERENCE_FIELD,
5445
+ filterExpression: {
5446
+ _type: "string",
5447
+ _list: false,
5448
+ eq: id
5449
+ }
5450
+ }
5451
+ ]
5452
+ }),
5453
+ sort: REFS_COLLECTIONS_SORT_KEY
5454
+ },
5455
+ (refId) => {
5456
+ count++;
5457
+ return refId;
5389
5458
  }
5459
+ );
5460
+ if (count) {
5461
+ return true;
5390
5462
  }
5391
5463
  return false;
5392
5464
  };
@@ -5394,46 +5466,41 @@ var Resolver = class {
5394
5466
  * Finds references to a document
5395
5467
  * @param id the id of the document to find references to
5396
5468
  * @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
5469
+ * @returns a map of references to the document
5398
5470
  */
5399
5471
  this.findReferences = async (id, c) => {
5400
5472
  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] = [];
5473
+ await this.database.query(
5474
+ {
5475
+ collection: c.name,
5476
+ filterChain: makeFilterChain({
5477
+ conditions: [
5478
+ {
5479
+ filterPath: REFS_REFERENCE_FIELD,
5480
+ filterExpression: {
5481
+ _type: "string",
5482
+ _list: false,
5483
+ eq: id
5484
+ }
5427
5485
  }
5428
- references[collection][refId].push({
5429
- path: ref.path,
5430
- field: ref.field
5431
- });
5432
- return refId;
5433
- }
5434
- );
5486
+ ]
5487
+ }),
5488
+ sort: REFS_COLLECTIONS_SORT_KEY
5489
+ },
5490
+ (refId, rawItem) => {
5491
+ if (!references[c.name]) {
5492
+ references[c.name] = {};
5493
+ }
5494
+ if (!references[c.name][refId]) {
5495
+ references[c.name][refId] = [];
5496
+ }
5497
+ const referencePath = rawItem?.[REFS_PATH_FIELD];
5498
+ if (referencePath) {
5499
+ references[c.name][refId].push(referencePath);
5500
+ }
5501
+ return refId;
5435
5502
  }
5436
- }
5503
+ );
5437
5504
  return references;
5438
5505
  };
5439
5506
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
@@ -5503,7 +5570,7 @@ var Resolver = class {
5503
5570
  }
5504
5571
  break;
5505
5572
  case "rich-text":
5506
- accum[fieldName] = stringifyMDX(
5573
+ accum[fieldName] = serializeMDX(
5507
5574
  fieldValue,
5508
5575
  field,
5509
5576
  (fieldValue2) => resolveMediaCloudToRelative(
@@ -5610,8 +5677,129 @@ var resolveDateInput = (value) => {
5610
5677
  return date;
5611
5678
  };
5612
5679
 
5613
- // src/resolve.ts
5680
+ // src/resolver/auth-fields.ts
5614
5681
  import set from "lodash.set";
5682
+ async function getUserDocumentContext(tinaSchema, resolver) {
5683
+ const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5684
+ if (!collection) {
5685
+ throw new Error("Auth collection not found");
5686
+ }
5687
+ const userFields = mapUserFields(collection, ["_rawData"]);
5688
+ if (!userFields.length) {
5689
+ throw new Error(`No user field found in collection ${collection.name}`);
5690
+ }
5691
+ if (userFields.length > 1) {
5692
+ throw new Error(
5693
+ `Multiple user fields found in collection ${collection.name}`
5694
+ );
5695
+ }
5696
+ const userField = userFields[0];
5697
+ const realPath = `${collection.path}/index.json`;
5698
+ const userDoc = await resolver.getDocument(realPath);
5699
+ const users = get(userDoc, userField.path);
5700
+ if (!users) {
5701
+ throw new Error("No users found");
5702
+ }
5703
+ return { collection, userField, users, userDoc, realPath };
5704
+ }
5705
+ function findUserInCollection(users, userField, userSub) {
5706
+ const { idFieldName } = userField;
5707
+ if (!idFieldName) {
5708
+ throw new Error("No uid field found on user field");
5709
+ }
5710
+ return users.find((u) => u[idFieldName] === userSub) || null;
5711
+ }
5712
+ async function handleAuthenticate({
5713
+ tinaSchema,
5714
+ resolver,
5715
+ sub,
5716
+ password,
5717
+ ctxUser
5718
+ }) {
5719
+ const userSub = sub || ctxUser?.sub;
5720
+ const { userField, users } = await getUserDocumentContext(
5721
+ tinaSchema,
5722
+ resolver
5723
+ );
5724
+ const user = findUserInCollection(users, userField, userSub);
5725
+ if (!user) {
5726
+ return null;
5727
+ }
5728
+ const { passwordFieldName } = userField;
5729
+ const saltedHash = get(user, [passwordFieldName || "", "value"]);
5730
+ if (!saltedHash) {
5731
+ throw new Error("No password field found on user field");
5732
+ }
5733
+ const matches = await checkPasswordHash({
5734
+ saltedHash,
5735
+ password
5736
+ });
5737
+ return matches ? user : null;
5738
+ }
5739
+ async function handleAuthorize({
5740
+ tinaSchema,
5741
+ resolver,
5742
+ sub,
5743
+ ctxUser
5744
+ }) {
5745
+ const userSub = sub || ctxUser?.sub;
5746
+ const { userField, users } = await getUserDocumentContext(
5747
+ tinaSchema,
5748
+ resolver
5749
+ );
5750
+ const user = findUserInCollection(users, userField, userSub);
5751
+ return user ? user : null;
5752
+ }
5753
+ async function handleUpdatePassword({
5754
+ tinaSchema,
5755
+ resolver,
5756
+ password,
5757
+ ctxUser
5758
+ }) {
5759
+ if (!ctxUser?.sub) {
5760
+ throw new Error("Not authorized");
5761
+ }
5762
+ if (!password) {
5763
+ throw new Error("No password provided");
5764
+ }
5765
+ const { collection, userField, users, realPath } = await getUserDocumentContext(tinaSchema, resolver);
5766
+ const { idFieldName, passwordFieldName } = userField;
5767
+ const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5768
+ if (!user) {
5769
+ throw new Error("Not authorized");
5770
+ }
5771
+ user[passwordFieldName] = {
5772
+ value: password,
5773
+ passwordChangeRequired: false
5774
+ };
5775
+ const params = {};
5776
+ set(
5777
+ params,
5778
+ userField.path.slice(1),
5779
+ // remove _rawData from users path
5780
+ users.map((u) => {
5781
+ if (user[idFieldName] === u[idFieldName]) {
5782
+ return user;
5783
+ }
5784
+ return {
5785
+ // don't overwrite other users' passwords
5786
+ ...u,
5787
+ [passwordFieldName]: {
5788
+ ...u[passwordFieldName],
5789
+ value: ""
5790
+ }
5791
+ };
5792
+ })
5793
+ );
5794
+ await resolver.updateResolveDocument({
5795
+ collection,
5796
+ args: { params },
5797
+ realPath,
5798
+ isCollectionSpecific: true,
5799
+ isAddPendingDocument: false
5800
+ });
5801
+ return true;
5802
+ }
5615
5803
 
5616
5804
  // src/error.ts
5617
5805
  import { GraphQLError as GraphQLError3 } from "graphql";
@@ -5721,119 +5909,33 @@ var resolve = async ({
5721
5909
  );
5722
5910
  }
5723
5911
  }
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;
5912
+ if (info.fieldName === "authenticate") {
5913
+ return handleAuthenticate({
5914
+ tinaSchema,
5915
+ resolver,
5916
+ sub: args.sub,
5917
+ password: args.password,
5918
+ info,
5919
+ ctxUser
5920
+ });
5921
+ }
5922
+ if (info.fieldName === "authorize") {
5923
+ return handleAuthorize({
5924
+ tinaSchema,
5925
+ resolver,
5926
+ sub: args.sub,
5927
+ info,
5928
+ ctxUser
5929
+ });
5771
5930
  }
5772
5931
  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
5932
+ return handleUpdatePassword({
5933
+ tinaSchema,
5934
+ resolver,
5935
+ password: args.password,
5936
+ info,
5937
+ ctxUser
5835
5938
  });
5836
- return true;
5837
5939
  }
5838
5940
  if (!lookup) {
5839
5941
  return value;
@@ -6177,6 +6279,7 @@ var Database = class {
6177
6279
  );
6178
6280
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
6179
6281
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6282
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6180
6283
  const normalizedPath = normalizePath(filepath);
6181
6284
  if (!collection?.isDetached) {
6182
6285
  if (this.bridge) {
@@ -6205,6 +6308,14 @@ var Database = class {
6205
6308
  let delOps = [];
6206
6309
  if (!isGitKeep(normalizedPath, collection)) {
6207
6310
  putOps = [
6311
+ ...makeRefOpsForDocument(
6312
+ normalizedPath,
6313
+ collection?.name,
6314
+ collectionReferences,
6315
+ dataFields,
6316
+ "put",
6317
+ level
6318
+ ),
6208
6319
  ...makeIndexOpsForDocument(
6209
6320
  normalizedPath,
6210
6321
  collection?.name,
@@ -6228,6 +6339,14 @@ var Database = class {
6228
6339
  SUBLEVEL_OPTIONS
6229
6340
  ).get(normalizedPath);
6230
6341
  delOps = existingItem ? [
6342
+ ...makeRefOpsForDocument(
6343
+ normalizedPath,
6344
+ collection?.name,
6345
+ collectionReferences,
6346
+ existingItem,
6347
+ "del",
6348
+ level
6349
+ ),
6231
6350
  ...makeIndexOpsForDocument(
6232
6351
  normalizedPath,
6233
6352
  collection?.name,
@@ -6275,6 +6394,7 @@ var Database = class {
6275
6394
  );
6276
6395
  collectionIndexDefinitions = indexDefinitions?.[collectionName];
6277
6396
  }
6397
+ const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
6278
6398
  const normalizedPath = normalizePath(filepath);
6279
6399
  const dataFields = await this.formatBodyOnPayload(filepath, data);
6280
6400
  const collection = await this.collectionForPath(filepath);
@@ -6322,6 +6442,14 @@ var Database = class {
6322
6442
  let delOps = [];
6323
6443
  if (!isGitKeep(normalizedPath, collection)) {
6324
6444
  putOps = [
6445
+ ...makeRefOpsForDocument(
6446
+ normalizedPath,
6447
+ collectionName,
6448
+ collectionReferences,
6449
+ dataFields,
6450
+ "put",
6451
+ level
6452
+ ),
6325
6453
  ...makeIndexOpsForDocument(
6326
6454
  normalizedPath,
6327
6455
  collectionName,
@@ -6345,6 +6473,14 @@ var Database = class {
6345
6473
  SUBLEVEL_OPTIONS
6346
6474
  ).get(normalizedPath);
6347
6475
  delOps = existingItem ? [
6476
+ ...makeRefOpsForDocument(
6477
+ normalizedPath,
6478
+ collectionName,
6479
+ collectionReferences,
6480
+ existingItem,
6481
+ "del",
6482
+ level
6483
+ ),
6348
6484
  ...makeIndexOpsForDocument(
6349
6485
  normalizedPath,
6350
6486
  collectionName,
@@ -6387,8 +6523,7 @@ var Database = class {
6387
6523
  throw new TinaFetchError(`Error in PUT for ${filepath}`, {
6388
6524
  originalError: error,
6389
6525
  file: filepath,
6390
- collection: collectionName,
6391
- stack: error.stack
6526
+ collection: collectionName
6392
6527
  });
6393
6528
  }
6394
6529
  };
@@ -6507,6 +6642,22 @@ var Database = class {
6507
6642
  this.tinaSchema = await createSchema({ schema });
6508
6643
  return this.tinaSchema;
6509
6644
  };
6645
+ this.getCollectionReferences = async (level) => {
6646
+ if (this.collectionReferences) {
6647
+ return this.collectionReferences;
6648
+ }
6649
+ const result = {};
6650
+ const schema = await this.getSchema(level || this.contentLevel);
6651
+ const collections = schema.getCollections();
6652
+ for (const collection of collections) {
6653
+ const collectionReferences = this.tinaSchema.findReferencesFromCollection(
6654
+ collection.name
6655
+ );
6656
+ result[collection.name] = collectionReferences;
6657
+ }
6658
+ this.collectionReferences = result;
6659
+ return result;
6660
+ };
6510
6661
  this.getIndexDefinitions = async (level) => {
6511
6662
  if (!this.collectionIndexDefinitions) {
6512
6663
  await new Promise(async (resolve2, reject) => {
@@ -6516,11 +6667,53 @@ var Database = class {
6516
6667
  const collections = schema.getCollections();
6517
6668
  for (const collection of collections) {
6518
6669
  const indexDefinitions = {
6519
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6670
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6520
6671
  // provide a default sort key which is the file sort
6672
+ // pseudo-index for the collection's references
6673
+ [REFS_COLLECTIONS_SORT_KEY]: {
6674
+ fields: [
6675
+ {
6676
+ name: REFS_REFERENCE_FIELD,
6677
+ type: "string",
6678
+ list: false
6679
+ },
6680
+ {
6681
+ name: REFS_PATH_FIELD,
6682
+ type: "string",
6683
+ list: false
6684
+ }
6685
+ ]
6686
+ }
6521
6687
  };
6522
- if (collection.fields) {
6523
- for (const field of collection.fields) {
6688
+ let fields = [];
6689
+ if (collection.templates) {
6690
+ const templateFieldMap = {};
6691
+ const conflictedFields = /* @__PURE__ */ new Set();
6692
+ for (const template of collection.templates) {
6693
+ for (const field of template.fields) {
6694
+ if (!templateFieldMap[field.name]) {
6695
+ templateFieldMap[field.name] = field;
6696
+ } else {
6697
+ if (templateFieldMap[field.name].type !== field.type) {
6698
+ console.warn(
6699
+ `Field ${field.name} has conflicting types in templates - skipping index`
6700
+ );
6701
+ conflictedFields.add(field.name);
6702
+ }
6703
+ }
6704
+ }
6705
+ }
6706
+ for (const conflictedField in conflictedFields) {
6707
+ delete templateFieldMap[conflictedField];
6708
+ }
6709
+ for (const field of Object.values(templateFieldMap)) {
6710
+ fields.push(field);
6711
+ }
6712
+ } else if (collection.fields) {
6713
+ fields = collection.fields;
6714
+ }
6715
+ if (fields) {
6716
+ for (const field of fields) {
6524
6717
  if (field.indexed !== void 0 && field.indexed === false || field.type === "object") {
6525
6718
  continue;
6526
6719
  }
@@ -6668,29 +6861,35 @@ var Database = class {
6668
6861
  }
6669
6862
  startKey = startKey || key || "";
6670
6863
  endKey = key || "";
6671
- edges = [...edges, { cursor: key, path: filepath }];
6864
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6672
6865
  }
6673
6866
  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
- });
6867
+ edges: await sequential(
6868
+ edges,
6869
+ async ({
6870
+ cursor,
6871
+ path: path7,
6872
+ value
6873
+ }) => {
6874
+ try {
6875
+ const node = await hydrator(path7, value);
6876
+ return {
6877
+ node,
6878
+ cursor: btoa(cursor)
6879
+ };
6880
+ } catch (error) {
6881
+ console.log(error);
6882
+ if (error instanceof Error && (!path7.includes(".tina/__generated__/_graphql.json") || !path7.includes("tina/__generated__/_graphql.json"))) {
6883
+ throw new TinaQueryError({
6884
+ originalError: error,
6885
+ file: path7,
6886
+ collection: collection.name
6887
+ });
6888
+ }
6889
+ throw error;
6690
6890
  }
6691
- throw error;
6692
6891
  }
6693
- }),
6892
+ ),
6694
6893
  pageInfo: {
6695
6894
  hasPreviousPage,
6696
6895
  hasNextPage,
@@ -6814,13 +7013,14 @@ var Database = class {
6814
7013
  documentPaths,
6815
7014
  async (collection, documentPaths2) => {
6816
7015
  if (collection && !collection.isDetached) {
6817
- await _indexContent(
6818
- this,
6819
- this.contentLevel,
6820
- documentPaths2,
7016
+ await _indexContent({
7017
+ database: this,
7018
+ level: this.contentLevel,
7019
+ documentPaths: documentPaths2,
6821
7020
  enqueueOps,
6822
- collection
6823
- );
7021
+ collection,
7022
+ isPartialReindex: true
7023
+ });
6824
7024
  }
6825
7025
  }
6826
7026
  );
@@ -6836,6 +7036,7 @@ var Database = class {
6836
7036
  throw new Error(`No collection found for path: ${filepath}`);
6837
7037
  }
6838
7038
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7039
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6839
7040
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6840
7041
  let level = this.contentLevel;
6841
7042
  if (collection?.isDetached) {
@@ -6854,6 +7055,14 @@ var Database = class {
6854
7055
  collection.path || ""
6855
7056
  );
6856
7057
  await this.contentLevel.batch([
7058
+ ...makeRefOpsForDocument(
7059
+ normalizedPath,
7060
+ collection.name,
7061
+ collectionReferences,
7062
+ item,
7063
+ "del",
7064
+ level
7065
+ ),
6857
7066
  ...makeIndexOpsForDocument(
6858
7067
  normalizedPath,
6859
7068
  collection.name,
@@ -6918,20 +7127,26 @@ var Database = class {
6918
7127
  );
6919
7128
  const doc = await level2.keys({ limit: 1 }).next();
6920
7129
  if (!doc) {
6921
- await _indexContent(
6922
- this,
6923
- level2,
6924
- contentPaths,
7130
+ await _indexContent({
7131
+ database: this,
7132
+ level: level2,
7133
+ documentPaths: contentPaths,
6925
7134
  enqueueOps,
6926
7135
  collection,
6927
- userFields.map((field) => [
7136
+ passwordFields: userFields.map((field) => [
6928
7137
  ...field.path,
6929
7138
  field.passwordFieldName
6930
7139
  ])
6931
- );
7140
+ });
6932
7141
  }
6933
7142
  } else {
6934
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7143
+ await _indexContent({
7144
+ database: this,
7145
+ level,
7146
+ documentPaths: contentPaths,
7147
+ enqueueOps,
7148
+ collection
7149
+ });
6935
7150
  }
6936
7151
  }
6937
7152
  );
@@ -7070,7 +7285,15 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
7070
7285
  )
7071
7286
  );
7072
7287
  var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${collection?.format || "md"}`);
7073
- var _indexContent = async (database, level, documentPaths, enqueueOps, collection, passwordFields) => {
7288
+ var _indexContent = async ({
7289
+ database,
7290
+ level,
7291
+ documentPaths,
7292
+ enqueueOps,
7293
+ collection,
7294
+ passwordFields,
7295
+ isPartialReindex
7296
+ }) => {
7074
7297
  let collectionIndexDefinitions;
7075
7298
  let collectionPath;
7076
7299
  if (collection) {
@@ -7081,6 +7304,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7081
7304
  }
7082
7305
  collectionPath = collection.path;
7083
7306
  }
7307
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
7084
7308
  const tinaSchema = await database.getSchema();
7085
7309
  let templateInfo = null;
7086
7310
  if (collection) {
@@ -7102,12 +7326,61 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7102
7326
  await hashPasswordValues(aliasedData, passwordFields);
7103
7327
  }
7104
7328
  const normalizedPath = normalizePath(filepath);
7329
+ const rootSublevel = level.sublevel(
7330
+ CONTENT_ROOT_PREFIX,
7331
+ SUBLEVEL_OPTIONS
7332
+ );
7105
7333
  const folderKey = folderTreeBuilder.update(
7106
7334
  normalizedPath,
7107
7335
  collectionPath || ""
7108
7336
  );
7337
+ if (isPartialReindex) {
7338
+ const item = await rootSublevel.get(normalizedPath);
7339
+ if (item) {
7340
+ await database.contentLevel.batch([
7341
+ ...makeRefOpsForDocument(
7342
+ normalizedPath,
7343
+ collection?.name,
7344
+ collectionReferences,
7345
+ item,
7346
+ "del",
7347
+ level
7348
+ ),
7349
+ ...makeIndexOpsForDocument(
7350
+ normalizedPath,
7351
+ collection.name,
7352
+ collectionIndexDefinitions,
7353
+ item,
7354
+ "del",
7355
+ level
7356
+ ),
7357
+ // folder indices
7358
+ ...makeIndexOpsForDocument(
7359
+ normalizedPath,
7360
+ `${collection.name}_${folderKey}`,
7361
+ collectionIndexDefinitions,
7362
+ item,
7363
+ "del",
7364
+ level
7365
+ ),
7366
+ {
7367
+ type: "del",
7368
+ key: normalizedPath,
7369
+ sublevel: rootSublevel
7370
+ }
7371
+ ]);
7372
+ }
7373
+ }
7109
7374
  if (!isGitKeep(filepath, collection)) {
7110
7375
  await enqueueOps([
7376
+ ...makeRefOpsForDocument(
7377
+ normalizedPath,
7378
+ collection?.name,
7379
+ collectionReferences,
7380
+ aliasedData,
7381
+ "put",
7382
+ level
7383
+ ),
7111
7384
  ...makeIndexOpsForDocument(
7112
7385
  normalizedPath,
7113
7386
  collection?.name,
@@ -7140,8 +7413,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7140
7413
  throw new TinaFetchError(`Unable to seed ${filepath}`, {
7141
7414
  originalError: error,
7142
7415
  file: filepath,
7143
- collection: collection?.name,
7144
- stack: error.stack
7416
+ collection: collection?.name
7145
7417
  });
7146
7418
  }
7147
7419
  });
@@ -7171,6 +7443,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7171
7443
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
7172
7444
  }
7173
7445
  }
7446
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
7174
7447
  const tinaSchema = await database.getSchema();
7175
7448
  let templateInfo = null;
7176
7449
  if (collection) {
@@ -7194,6 +7467,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7194
7467
  item
7195
7468
  ) : item;
7196
7469
  await enqueueOps([
7470
+ ...makeRefOpsForDocument(
7471
+ itemKey,
7472
+ collection?.name,
7473
+ collectionReferences,
7474
+ aliasedData,
7475
+ "del",
7476
+ database.contentLevel
7477
+ ),
7197
7478
  ...makeIndexOpsForDocument(
7198
7479
  itemKey,
7199
7480
  collection.name,
@@ -7323,8 +7604,8 @@ import path6 from "path";
7323
7604
  import normalize from "normalize-path";
7324
7605
  var FilesystemBridge = class {
7325
7606
  constructor(rootPath, outputPath) {
7326
- this.rootPath = rootPath || "";
7327
- this.outputPath = outputPath || rootPath;
7607
+ this.rootPath = path6.resolve(rootPath);
7608
+ this.outputPath = outputPath ? path6.resolve(outputPath) : this.rootPath;
7328
7609
  }
7329
7610
  async glob(pattern, extension) {
7330
7611
  const basePath = path6.join(this.outputPath, ...pattern.split("/"));
@@ -7336,19 +7617,19 @@ var FilesystemBridge = class {
7336
7617
  }
7337
7618
  );
7338
7619
  const posixRootPath = normalize(this.outputPath);
7339
- return items.map((item) => {
7340
- return item.replace(posixRootPath, "").replace(/^\/|\/$/g, "");
7341
- });
7620
+ return items.map(
7621
+ (item) => item.substring(posixRootPath.length).replace(/^\/|\/$/g, "")
7622
+ );
7342
7623
  }
7343
7624
  async delete(filepath) {
7344
7625
  await fs2.remove(path6.join(this.outputPath, filepath));
7345
7626
  }
7346
7627
  async get(filepath) {
7347
- return fs2.readFileSync(path6.join(this.outputPath, filepath)).toString();
7628
+ return (await fs2.readFile(path6.join(this.outputPath, filepath))).toString();
7348
7629
  }
7349
7630
  async put(filepath, data, basePathOverride) {
7350
7631
  const basePath = basePathOverride || this.outputPath;
7351
- await fs2.outputFileSync(path6.join(basePath, filepath), data);
7632
+ await fs2.outputFile(path6.join(basePath, filepath), data);
7352
7633
  }
7353
7634
  };
7354
7635
  var AuditFileSystemBridge = class extends FilesystemBridge {