@tinacms/graphql 0.0.0-d7c5ec1-20250219020924 → 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
@@ -1847,7 +1847,7 @@ var Builder = class {
1847
1847
  * ```
1848
1848
  *
1849
1849
  * @public
1850
- * @param collection a Tina Cloud collection
1850
+ * @param collection a TinaCloud collection
1851
1851
  */
1852
1852
  this.collectionFragment = async (collection) => {
1853
1853
  const name = NAMER.dataTypeName(collection.namespace);
@@ -2711,7 +2711,7 @@ var Builder = class {
2711
2711
  this._buildDataField = async (field) => {
2712
2712
  const listWarningMsg = `
2713
2713
  WARNING: The user interface for ${field.type} does not support \`list: true\`
2714
- 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
2715
2715
 
2716
2716
  `;
2717
2717
  switch (field.type) {
@@ -3019,7 +3019,7 @@ var validateField = async (field) => {
3019
3019
  // package.json
3020
3020
  var package_default = {
3021
3021
  name: "@tinacms/graphql",
3022
- version: "1.5.12",
3022
+ version: "1.6.1",
3023
3023
  main: "dist/index.js",
3024
3024
  module: "dist/index.mjs",
3025
3025
  typings: "dist/index.d.ts",
@@ -3045,33 +3045,32 @@ var package_default = {
3045
3045
  types: "pnpm tsc",
3046
3046
  build: "tinacms-scripts build",
3047
3047
  docs: "pnpm typedoc",
3048
- serve: "pnpm nodemon dist/server.js",
3049
3048
  test: "vitest run",
3050
3049
  "test-watch": "vitest"
3051
3050
  },
3052
3051
  dependencies: {
3053
- "@iarna/toml": "^2.2.5",
3052
+ "@iarna/toml": "catalog:",
3054
3053
  "@tinacms/mdx": "workspace:*",
3055
3054
  "@tinacms/schema-tools": "workspace:*",
3056
- "abstract-level": "^1.0.4",
3055
+ "abstract-level": "catalog:",
3057
3056
  "date-fns": "^2.30.0",
3058
- "fast-glob": "^3.3.3",
3059
- "fs-extra": "^11.3.0",
3060
- "glob-parent": "^6.0.2",
3057
+ "fast-glob": "catalog:",
3058
+ "fs-extra": "catalog:",
3059
+ "glob-parent": "catalog:",
3061
3060
  graphql: "15.8.0",
3062
- "gray-matter": "^4.0.3",
3063
- "isomorphic-git": "^1.29.0",
3064
- "js-sha1": "^0.6.0",
3061
+ "gray-matter": "catalog:",
3062
+ "isomorphic-git": "catalog:",
3063
+ "js-sha1": "catalog:",
3065
3064
  "js-yaml": "^3.14.1",
3066
- "jsonpath-plus": "10.1.0",
3067
- "lodash.clonedeep": "^4.5.0",
3068
- "lodash.set": "^4.3.2",
3069
- "lodash.uniqby": "^4.7.0",
3070
- "many-level": "^2.0.0",
3071
- micromatch: "4.0.8",
3072
- "normalize-path": "^3.0.0",
3073
- "readable-stream": "^4.7.0",
3074
- 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:",
3075
3074
  yup: "^0.32.11"
3076
3075
  },
3077
3076
  publishConfig: {
@@ -3086,25 +3085,24 @@ var package_default = {
3086
3085
  "@tinacms/scripts": "workspace:*",
3087
3086
  "@types/cors": "^2.8.17",
3088
3087
  "@types/estree": "^0.0.50",
3089
- "@types/express": "^4.17.21",
3088
+ "@types/express": "catalog:",
3090
3089
  "@types/fs-extra": "^9.0.13",
3091
3090
  "@types/js-yaml": "^3.12.10",
3092
- "@types/lodash.camelcase": "^4.3.9",
3093
- "@types/lodash.upperfirst": "^4.3.9",
3094
- "@types/lru-cache": "^5.1.1",
3095
- "@types/mdast": "^3.0.15",
3096
- "@types/micromatch": "^4.0.9",
3091
+ "@types/lodash.camelcase": "catalog:",
3092
+ "@types/lodash.upperfirst": "catalog:",
3093
+ "@types/lru-cache": "catalog:",
3094
+ "@types/mdast": "catalog:",
3095
+ "@types/micromatch": "catalog:",
3097
3096
  "@types/node": "^22.13.1",
3098
- "@types/normalize-path": "^3.0.2",
3099
- "@types/ws": "^7.4.7",
3097
+ "@types/normalize-path": "catalog:",
3098
+ "@types/ws": "catalog:",
3100
3099
  "@types/yup": "^0.29.14",
3101
3100
  "jest-file-snapshot": "^0.5.0",
3102
- "memory-level": "^1.0.0",
3103
- nodemon: "3.1.4",
3101
+ "memory-level": "catalog:",
3104
3102
  typescript: "^5.7.3",
3105
3103
  vite: "^4.5.9",
3106
3104
  vitest: "^0.32.4",
3107
- zod: "^3.24.2"
3105
+ zod: "catalog:"
3108
3106
  }
3109
3107
  };
3110
3108
 
@@ -3260,7 +3258,9 @@ var _buildSchema = async (builder, tinaSchema) => {
3260
3258
  await builder.buildCreateCollectionFolderMutation()
3261
3259
  );
3262
3260
  await sequential(collections, async (collection) => {
3263
- queryTypeDefinitionFields.push(await builder.collectionDocument(collection));
3261
+ queryTypeDefinitionFields.push(
3262
+ await builder.collectionDocument(collection)
3263
+ );
3264
3264
  if (collection.isAuthCollection) {
3265
3265
  queryTypeDefinitionFields.push(
3266
3266
  await builder.authenticationCollectionDocument(collection)
@@ -3310,249 +3310,10 @@ import { graphql, buildASTSchema, getNamedType, GraphQLError as GraphQLError4 }
3310
3310
  // src/resolver/index.ts
3311
3311
  import path3 from "path";
3312
3312
  import isValid from "date-fns/isValid/index.js";
3313
-
3314
- // src/mdx/index.ts
3315
- import { parseMDX, stringifyMDX } from "@tinacms/mdx";
3316
-
3317
- // src/resolver/index.ts
3318
3313
  import { JSONPath as JSONPath2 } from "jsonpath-plus";
3319
3314
 
3320
- // src/resolver/error.ts
3321
- var TinaGraphQLError = class extends Error {
3322
- constructor(message, extensions) {
3323
- super(message);
3324
- if (!this.name) {
3325
- Object.defineProperty(this, "name", { value: "TinaGraphQLError" });
3326
- }
3327
- this.extensions = { ...extensions };
3328
- }
3329
- };
3330
- var TinaFetchError = class extends Error {
3331
- constructor(message, args) {
3332
- super(message);
3333
- this.name = "TinaFetchError";
3334
- this.collection = args.collection;
3335
- this.stack = args.stack;
3336
- this.file = args.file;
3337
- this.originalError = args.originalError;
3338
- }
3339
- };
3340
- var TinaQueryError = class extends TinaFetchError {
3341
- constructor(args) {
3342
- super(
3343
- `Error querying file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3344
- args
3345
- );
3346
- }
3347
- };
3348
- var TinaParseDocumentError = class extends TinaFetchError {
3349
- constructor(args) {
3350
- super(
3351
- `Error parsing file ${args.file} from collection ${args.collection}. ${auditMessage(args.includeAuditMessage)}`,
3352
- args
3353
- );
3354
- }
3355
- toString() {
3356
- return super.toString() + "\n OriginalError: \n" + this.originalError.toString();
3357
- }
3358
- };
3359
- var auditMessage = (includeAuditMessage = true) => includeAuditMessage ? `Please run "tinacms audit" or add the --verbose option for more info` : "";
3360
- var handleFetchErrorError = (e, verbose) => {
3361
- if (e instanceof Error) {
3362
- if (e instanceof TinaFetchError) {
3363
- if (verbose) {
3364
- console.log(e.toString());
3365
- console.log(e);
3366
- console.log(e.stack);
3367
- }
3368
- }
3369
- } else {
3370
- console.error(e);
3371
- }
3372
- throw e;
3373
- };
3374
-
3375
- // src/resolver/filter-utils.ts
3376
- var resolveReferences = async (filter, fields, resolver) => {
3377
- for (const fieldKey of Object.keys(filter)) {
3378
- const fieldDefinition = fields.find(
3379
- (f) => f.name === fieldKey
3380
- );
3381
- if (fieldDefinition) {
3382
- if (fieldDefinition.type === "reference") {
3383
- const { edges, values } = await resolver(filter, fieldDefinition);
3384
- if (edges.length === 1) {
3385
- filter[fieldKey] = {
3386
- eq: values[0]
3387
- };
3388
- } else if (edges.length > 1) {
3389
- filter[fieldKey] = {
3390
- in: values
3391
- };
3392
- } else {
3393
- filter[fieldKey] = {
3394
- eq: "___null___"
3395
- };
3396
- }
3397
- } else if (fieldDefinition.type === "object") {
3398
- if (fieldDefinition.templates) {
3399
- for (const templateName of Object.keys(filter[fieldKey])) {
3400
- const template = fieldDefinition.templates.find(
3401
- (template2) => !(typeof template2 === "string") && template2.name === templateName
3402
- );
3403
- if (template) {
3404
- await resolveReferences(
3405
- filter[fieldKey][templateName],
3406
- template.fields,
3407
- resolver
3408
- );
3409
- } else {
3410
- throw new Error(`Template ${templateName} not found`);
3411
- }
3412
- }
3413
- } else {
3414
- await resolveReferences(
3415
- filter[fieldKey],
3416
- fieldDefinition.fields,
3417
- resolver
3418
- );
3419
- }
3420
- }
3421
- } else {
3422
- throw new Error(`Unable to find field ${fieldKey}`);
3423
- }
3424
- }
3425
- };
3426
- var collectConditionsForChildFields = (filterNode, fields, pathExpression, collectCondition) => {
3427
- for (const childFieldName of Object.keys(filterNode)) {
3428
- const childField = fields.find((field) => field.name === childFieldName);
3429
- if (!childField) {
3430
- throw new Error(`Unable to find type for field ${childFieldName}`);
3431
- }
3432
- collectConditionsForField(
3433
- childFieldName,
3434
- childField,
3435
- filterNode[childFieldName],
3436
- pathExpression,
3437
- collectCondition
3438
- );
3439
- }
3440
- };
3441
- var collectConditionsForObjectField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3442
- if (field.list && field.templates) {
3443
- for (const [filterKey, childFilterNode] of Object.entries(filterNode)) {
3444
- const template = field.templates.find(
3445
- (template2) => !(typeof template2 === "string") && template2.name === filterKey
3446
- );
3447
- const jsonPath = `${fieldName}[?(@._template=="${filterKey}")]`;
3448
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : jsonPath;
3449
- collectConditionsForChildFields(
3450
- childFilterNode,
3451
- template.fields,
3452
- filterPath,
3453
- collectCondition
3454
- );
3455
- }
3456
- } else {
3457
- const jsonPath = `${fieldName}${field.list ? "[*]" : ""}`;
3458
- const filterPath = pathExpression ? `${pathExpression}.${jsonPath}` : `${jsonPath}`;
3459
- collectConditionsForChildFields(
3460
- filterNode,
3461
- field.fields,
3462
- filterPath,
3463
- collectCondition
3464
- );
3465
- }
3466
- };
3467
- var collectConditionsForField = (fieldName, field, filterNode, pathExpression, collectCondition) => {
3468
- if (field.type === "object") {
3469
- collectConditionsForObjectField(
3470
- fieldName,
3471
- field,
3472
- filterNode,
3473
- pathExpression,
3474
- collectCondition
3475
- );
3476
- } else {
3477
- collectCondition({
3478
- filterPath: pathExpression ? `${pathExpression}.${fieldName}` : fieldName,
3479
- filterExpression: {
3480
- _type: field.type,
3481
- _list: !!field.list,
3482
- ...filterNode
3483
- }
3484
- });
3485
- }
3486
- };
3487
-
3488
- // src/resolver/media-utils.ts
3489
- var resolveMediaCloudToRelative = (value, config = { useRelativeMedia: true }, schema) => {
3490
- if (config && value) {
3491
- if (config.useRelativeMedia === true) {
3492
- return value;
3493
- }
3494
- if (hasTinaMediaConfig(schema) === true) {
3495
- const assetsURL = `https://${config.assetsHost}/${config.clientId}`;
3496
- if (typeof value === "string" && value.includes(assetsURL)) {
3497
- const cleanMediaRoot = cleanUpSlashes(
3498
- schema.config.media.tina.mediaRoot
3499
- );
3500
- const strippedURL = value.replace(assetsURL, "");
3501
- return `${cleanMediaRoot}${strippedURL}`;
3502
- }
3503
- if (Array.isArray(value)) {
3504
- return value.map((v) => {
3505
- if (!v || typeof v !== "string") return v;
3506
- const cleanMediaRoot = cleanUpSlashes(
3507
- schema.config.media.tina.mediaRoot
3508
- );
3509
- const strippedURL = v.replace(assetsURL, "");
3510
- return `${cleanMediaRoot}${strippedURL}`;
3511
- });
3512
- }
3513
- return value;
3514
- }
3515
- return value;
3516
- } else {
3517
- return value;
3518
- }
3519
- };
3520
- var resolveMediaRelativeToCloud = (value, config = { useRelativeMedia: true }, schema) => {
3521
- if (config && value) {
3522
- if (config.useRelativeMedia === true) {
3523
- return value;
3524
- }
3525
- if (hasTinaMediaConfig(schema) === true) {
3526
- const cleanMediaRoot = cleanUpSlashes(schema.config.media.tina.mediaRoot);
3527
- if (typeof value === "string") {
3528
- const strippedValue = value.replace(cleanMediaRoot, "");
3529
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3530
- }
3531
- if (Array.isArray(value)) {
3532
- return value.map((v) => {
3533
- if (!v || typeof v !== "string") return v;
3534
- const strippedValue = v.replace(cleanMediaRoot, "");
3535
- return `https://${config.assetsHost}/${config.clientId}${strippedValue}`;
3536
- });
3537
- }
3538
- }
3539
- return value;
3540
- } else {
3541
- return value;
3542
- }
3543
- };
3544
- var cleanUpSlashes = (path7) => {
3545
- if (path7) {
3546
- return `/${path7.replace(/^\/+|\/+$/gm, "")}`;
3547
- }
3548
- return "";
3549
- };
3550
- var hasTinaMediaConfig = (schema) => {
3551
- if (!schema.config?.media?.tina) return false;
3552
- if (typeof schema.config?.media?.tina?.publicFolder !== "string" && typeof schema.config?.media?.tina?.mediaRoot !== "string")
3553
- return false;
3554
- return true;
3555
- };
3315
+ // src/mdx/index.ts
3316
+ import { parseMDX, serializeMDX } from "@tinacms/mdx";
3556
3317
 
3557
3318
  // src/resolver/index.ts
3558
3319
  import { GraphQLError as GraphQLError2 } from "graphql";
@@ -3575,7 +3336,9 @@ var LevelProxyHandler = {
3575
3336
  throw new Error(`The property, ${property.toString()}, doesn't exist`);
3576
3337
  }
3577
3338
  if (typeof target[property] !== "function") {
3578
- 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
+ );
3579
3342
  }
3580
3343
  if (property === "get") {
3581
3344
  return async (...args) => {
@@ -3613,13 +3376,13 @@ import path2 from "path";
3613
3376
 
3614
3377
  // src/database/util.ts
3615
3378
  import toml from "@iarna/toml";
3616
- import yaml from "js-yaml";
3617
- import matter from "gray-matter";
3618
3379
  import {
3619
3380
  normalizePath
3620
3381
  } from "@tinacms/schema-tools";
3621
- import micromatch from "micromatch";
3382
+ import matter from "gray-matter";
3383
+ import yaml from "js-yaml";
3622
3384
  import path from "path";
3385
+ import micromatch from "micromatch";
3623
3386
 
3624
3387
  // src/database/alias-utils.ts
3625
3388
  var replaceBlockAliases = (template, item) => {
@@ -3960,6 +3723,9 @@ var loadAndParseWithAliases = async (bridge, filepath, collection, templateInfo)
3960
3723
 
3961
3724
  // src/database/datalayer.ts
3962
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__";
3963
3729
  var DEFAULT_NUMERIC_LPAD = 4;
3964
3730
  var applyPadding = (input, pad) => {
3965
3731
  if (pad) {
@@ -4533,12 +4299,63 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4533
4299
  }
4534
4300
  return result;
4535
4301
  };
4536
- var makeStringEscaper = (regex, replacement) => {
4537
- return (input) => {
4538
- if (Array.isArray(input)) {
4539
- return input.map(
4540
- (val) => val.replace(regex, replacement)
4541
- );
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
+ };
4353
+ var makeStringEscaper = (regex, replacement) => {
4354
+ return (input) => {
4355
+ if (Array.isArray(input)) {
4356
+ return input.map(
4357
+ (val) => val.replace(regex, replacement)
4358
+ );
4542
4359
  } else {
4543
4360
  if (typeof input === "string") {
4544
4361
  return input.replace(regex, replacement);
@@ -4548,10 +4365,246 @@ var makeStringEscaper = (regex, replacement) => {
4548
4365
  }
4549
4366
  };
4550
4367
  };
4551
- var stringEscaper = makeStringEscaper(
4552
- new RegExp(INDEX_KEY_FIELD_SEPARATOR, "gm"),
4553
- encodeURIComponent(INDEX_KEY_FIELD_SEPARATOR)
4554
- );
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;
4607
+ };
4555
4608
 
4556
4609
  // src/resolver/index.ts
4557
4610
  var createResolver = (args) => {
@@ -4713,8 +4766,7 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4713
4766
  originalError: e,
4714
4767
  collection: collection.name,
4715
4768
  includeAuditMessage: !isAudit,
4716
- file: relativePath,
4717
- stack: e.stack
4769
+ file: relativePath
4718
4770
  });
4719
4771
  }
4720
4772
  const titleField = template.fields.find((x) => {
@@ -4753,24 +4805,33 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
4753
4805
  throw e;
4754
4806
  }
4755
4807
  };
4756
- var updateObjectWithJsonPath = (obj, path7, newValue) => {
4808
+ var updateObjectWithJsonPath = (obj, path7, oldValue, newValue) => {
4809
+ let updated = false;
4757
4810
  if (!path7.includes(".") && !path7.includes("[")) {
4758
- if (path7 in obj) {
4811
+ if (path7 in obj && obj[path7] === oldValue) {
4759
4812
  obj[path7] = newValue;
4813
+ updated = true;
4760
4814
  }
4761
- return obj;
4762
- }
4763
- const parentPath = path7.replace(/\.[^.]+$/, "");
4764
- const keyToUpdate = path7.match(/[^.]+$/)[0];
4765
- 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
+ });
4766
4824
  if (parents.length > 0) {
4767
4825
  parents.forEach((parent) => {
4768
4826
  if (parent && typeof parent === "object" && keyToUpdate in parent) {
4769
- parent[keyToUpdate] = newValue;
4827
+ if (parent[keyToUpdate] === oldValue) {
4828
+ parent[keyToUpdate] = newValue;
4829
+ updated = true;
4830
+ }
4770
4831
  }
4771
4832
  });
4772
4833
  }
4773
- return obj;
4834
+ return { object: obj, updated };
4774
4835
  };
4775
4836
  var Resolver = class {
4776
4837
  constructor(init) {
@@ -4787,7 +4848,9 @@ var Resolver = class {
4787
4848
  };
4788
4849
  this.getRaw = async (fullPath) => {
4789
4850
  if (typeof fullPath !== "string") {
4790
- 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
+ );
4791
4854
  }
4792
4855
  return this.database.get(fullPath);
4793
4856
  };
@@ -4816,7 +4879,9 @@ var Resolver = class {
4816
4879
  };
4817
4880
  this.getDocument = async (fullPath, opts = {}) => {
4818
4881
  if (typeof fullPath !== "string") {
4819
- 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
+ );
4820
4885
  }
4821
4886
  const rawData = await this.getRaw(fullPath);
4822
4887
  const hasReferences = opts?.checkReferences ? await this.hasReferences(fullPath, opts.collection) : void 0;
@@ -4831,7 +4896,9 @@ var Resolver = class {
4831
4896
  };
4832
4897
  this.deleteDocument = async (fullPath) => {
4833
4898
  if (typeof fullPath !== "string") {
4834
- 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
+ );
4835
4902
  }
4836
4903
  await this.database.delete(fullPath);
4837
4904
  };
@@ -5035,7 +5102,11 @@ var Resolver = class {
5035
5102
  collection,
5036
5103
  doc?._rawData
5037
5104
  );
5038
- await this.database.put(realPath, { ...oldDoc, ...params }, collection.name);
5105
+ await this.database.put(
5106
+ realPath,
5107
+ { ...oldDoc, ...params },
5108
+ collection.name
5109
+ );
5039
5110
  return this.getDocument(realPath);
5040
5111
  };
5041
5112
  /**
@@ -5148,17 +5219,35 @@ var Resolver = class {
5148
5219
  await this.deleteDocument(realPath);
5149
5220
  if (await this.hasReferences(realPath, collection)) {
5150
5221
  const collRefs = await this.findReferences(realPath, collection);
5151
- for (const [collection2, refFields] of Object.entries(collRefs)) {
5152
- for (const [refPath, refs] of Object.entries(refFields)) {
5153
- let refDoc = await this.getRaw(refPath);
5154
- for (const ref of refs) {
5155
- 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(
5156
5230
  refDoc,
5157
- ref.path.join("."),
5231
+ path7,
5232
+ realPath,
5158
5233
  null
5159
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
+ );
5160
5250
  }
5161
- await this.database.put(refPath, refDoc, collection2);
5162
5251
  }
5163
5252
  }
5164
5253
  }
@@ -5184,23 +5273,43 @@ var Resolver = class {
5184
5273
  await this.database.put(newRealPath, doc._rawData, collection.name);
5185
5274
  await this.deleteDocument(realPath);
5186
5275
  const collRefs = await this.findReferences(realPath, collection);
5187
- for (const [collection2, refFields] of Object.entries(collRefs)) {
5188
- for (const [refPath, refs] of Object.entries(refFields)) {
5189
- let refDoc = await this.getRaw(refPath);
5190
- for (const ref of refs) {
5191
- refDoc = updateObjectWithJsonPath(
5192
- refDoc,
5193
- 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,
5194
5287
  newRealPath
5195
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
+ );
5196
5304
  }
5197
- await this.database.put(refPath, refDoc, collection2);
5198
5305
  }
5199
5306
  }
5200
5307
  return this.getDocument(newRealPath);
5201
5308
  }
5202
5309
  if (alreadyExists === false) {
5203
- 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
+ );
5204
5313
  }
5205
5314
  return this.updateResolveDocument({
5206
5315
  collection,
@@ -5326,35 +5435,30 @@ var Resolver = class {
5326
5435
  */
5327
5436
  this.hasReferences = async (id, c) => {
5328
5437
  let count = 0;
5329
- const deepRefs = this.tinaSchema.findReferences(c.name);
5330
- for (const [collection, refs] of Object.entries(deepRefs)) {
5331
- for (const ref of refs) {
5332
- await this.database.query(
5333
- {
5334
- collection,
5335
- filterChain: makeFilterChain({
5336
- conditions: [
5337
- {
5338
- filterPath: ref.path.join("."),
5339
- filterExpression: {
5340
- _type: "reference",
5341
- _list: false,
5342
- eq: id
5343
- }
5344
- }
5345
- ]
5346
- }),
5347
- sort: ref.field.name
5348
- },
5349
- (refId) => {
5350
- count++;
5351
- return refId;
5352
- }
5353
- );
5354
- if (count) {
5355
- return true;
5356
- }
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;
5357
5458
  }
5459
+ );
5460
+ if (count) {
5461
+ return true;
5358
5462
  }
5359
5463
  return false;
5360
5464
  };
@@ -5362,46 +5466,41 @@ var Resolver = class {
5362
5466
  * Finds references to a document
5363
5467
  * @param id the id of the document to find references to
5364
5468
  * @param c the collection to find references in
5365
- * @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
5366
5470
  */
5367
5471
  this.findReferences = async (id, c) => {
5368
5472
  const references = {};
5369
- const deepRefs = this.tinaSchema.findReferences(c.name);
5370
- for (const [collection, refs] of Object.entries(deepRefs)) {
5371
- for (const ref of refs) {
5372
- await this.database.query(
5373
- {
5374
- collection,
5375
- filterChain: makeFilterChain({
5376
- conditions: [
5377
- {
5378
- filterPath: ref.path.join("."),
5379
- filterExpression: {
5380
- _type: "reference",
5381
- _list: false,
5382
- eq: id
5383
- }
5384
- }
5385
- ]
5386
- }),
5387
- sort: ref.field.name
5388
- },
5389
- (refId) => {
5390
- if (!references[collection]) {
5391
- references[collection] = {};
5392
- }
5393
- if (!references[collection][refId]) {
5394
- 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
+ }
5395
5485
  }
5396
- references[collection][refId].push({
5397
- path: ref.path,
5398
- field: ref.field
5399
- });
5400
- return refId;
5401
- }
5402
- );
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;
5403
5502
  }
5404
- }
5503
+ );
5405
5504
  return references;
5406
5505
  };
5407
5506
  this.buildFieldMutations = async (fieldParams, template, existingData) => {
@@ -5471,7 +5570,7 @@ var Resolver = class {
5471
5570
  }
5472
5571
  break;
5473
5572
  case "rich-text":
5474
- accum[fieldName] = stringifyMDX(
5573
+ accum[fieldName] = serializeMDX(
5475
5574
  fieldValue,
5476
5575
  field,
5477
5576
  (fieldValue2) => resolveMediaCloudToRelative(
@@ -5578,8 +5677,129 @@ var resolveDateInput = (value) => {
5578
5677
  return date;
5579
5678
  };
5580
5679
 
5581
- // src/resolve.ts
5680
+ // src/resolver/auth-fields.ts
5582
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
+ }
5583
5803
 
5584
5804
  // src/error.ts
5585
5805
  import { GraphQLError as GraphQLError3 } from "graphql";
@@ -5689,119 +5909,33 @@ var resolve = async ({
5689
5909
  );
5690
5910
  }
5691
5911
  }
5692
- if (info.fieldName === "authenticate" || info.fieldName === "authorize") {
5693
- const sub = args.sub || ctxUser?.sub;
5694
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5695
- if (!collection) {
5696
- throw new Error("Auth collection not found");
5697
- }
5698
- const userFields = mapUserFields(collection, ["_rawData"]);
5699
- if (!userFields.length) {
5700
- throw new Error(
5701
- `No user field found in collection ${collection.name}`
5702
- );
5703
- }
5704
- if (userFields.length > 1) {
5705
- throw new Error(
5706
- `Multiple user fields found in collection ${collection.name}`
5707
- );
5708
- }
5709
- const userField = userFields[0];
5710
- const realPath = `${collection.path}/index.json`;
5711
- const userDoc = await resolver.getDocument(realPath);
5712
- const users = get(userDoc, userField.path);
5713
- if (!users) {
5714
- throw new Error("No users found");
5715
- }
5716
- const { idFieldName, passwordFieldName } = userField;
5717
- if (!idFieldName) {
5718
- throw new Error("No uid field found on user field");
5719
- }
5720
- const user = users.find((u) => u[idFieldName] === sub);
5721
- if (!user) {
5722
- return null;
5723
- }
5724
- if (info.fieldName === "authenticate") {
5725
- const saltedHash = get(user, [passwordFieldName || "", "value"]);
5726
- if (!saltedHash) {
5727
- throw new Error("No password field found on user field");
5728
- }
5729
- const matches = await checkPasswordHash({
5730
- saltedHash,
5731
- password: args.password
5732
- });
5733
- if (matches) {
5734
- return user;
5735
- }
5736
- return null;
5737
- }
5738
- 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
+ });
5739
5930
  }
5740
5931
  if (info.fieldName === "updatePassword") {
5741
- if (!ctxUser?.sub) {
5742
- throw new Error("Not authorized");
5743
- }
5744
- if (!args.password) {
5745
- throw new Error("No password provided");
5746
- }
5747
- const collection = tinaSchema.getCollections().find((c) => c.isAuthCollection);
5748
- if (!collection) {
5749
- throw new Error("Auth collection not found");
5750
- }
5751
- const userFields = mapUserFields(collection, ["_rawData"]);
5752
- if (!userFields.length) {
5753
- throw new Error(
5754
- `No user field found in collection ${collection.name}`
5755
- );
5756
- }
5757
- if (userFields.length > 1) {
5758
- throw new Error(
5759
- `Multiple user fields found in collection ${collection.name}`
5760
- );
5761
- }
5762
- const userField = userFields[0];
5763
- const realPath = `${collection.path}/index.json`;
5764
- const userDoc = await resolver.getDocument(realPath);
5765
- const users = get(userDoc, userField.path);
5766
- if (!users) {
5767
- throw new Error("No users found");
5768
- }
5769
- const { idFieldName, passwordFieldName } = userField;
5770
- const user = users.find((u) => u[idFieldName] === ctxUser.sub);
5771
- if (!user) {
5772
- throw new Error("Not authorized");
5773
- }
5774
- user[passwordFieldName] = {
5775
- value: args.password,
5776
- passwordChangeRequired: false
5777
- };
5778
- const params = {};
5779
- set(
5780
- params,
5781
- userField.path.slice(1),
5782
- // remove _rawData from users path
5783
- users.map((u) => {
5784
- if (user[idFieldName] === u[idFieldName]) {
5785
- return user;
5786
- }
5787
- return {
5788
- // don't overwrite other users' passwords
5789
- ...u,
5790
- [passwordFieldName]: {
5791
- ...u[passwordFieldName],
5792
- value: ""
5793
- }
5794
- };
5795
- })
5796
- );
5797
- await resolver.updateResolveDocument({
5798
- collection,
5799
- args: { params },
5800
- realPath,
5801
- isCollectionSpecific: true,
5802
- isAddPendingDocument: false
5932
+ return handleUpdatePassword({
5933
+ tinaSchema,
5934
+ resolver,
5935
+ password: args.password,
5936
+ info,
5937
+ ctxUser
5803
5938
  });
5804
- return true;
5805
5939
  }
5806
5940
  if (!lookup) {
5807
5941
  return value;
@@ -6145,6 +6279,7 @@ var Database = class {
6145
6279
  );
6146
6280
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
6147
6281
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6282
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6148
6283
  const normalizedPath = normalizePath(filepath);
6149
6284
  if (!collection?.isDetached) {
6150
6285
  if (this.bridge) {
@@ -6173,6 +6308,14 @@ var Database = class {
6173
6308
  let delOps = [];
6174
6309
  if (!isGitKeep(normalizedPath, collection)) {
6175
6310
  putOps = [
6311
+ ...makeRefOpsForDocument(
6312
+ normalizedPath,
6313
+ collection?.name,
6314
+ collectionReferences,
6315
+ dataFields,
6316
+ "put",
6317
+ level
6318
+ ),
6176
6319
  ...makeIndexOpsForDocument(
6177
6320
  normalizedPath,
6178
6321
  collection?.name,
@@ -6196,6 +6339,14 @@ var Database = class {
6196
6339
  SUBLEVEL_OPTIONS
6197
6340
  ).get(normalizedPath);
6198
6341
  delOps = existingItem ? [
6342
+ ...makeRefOpsForDocument(
6343
+ normalizedPath,
6344
+ collection?.name,
6345
+ collectionReferences,
6346
+ existingItem,
6347
+ "del",
6348
+ level
6349
+ ),
6199
6350
  ...makeIndexOpsForDocument(
6200
6351
  normalizedPath,
6201
6352
  collection?.name,
@@ -6243,6 +6394,7 @@ var Database = class {
6243
6394
  );
6244
6395
  collectionIndexDefinitions = indexDefinitions?.[collectionName];
6245
6396
  }
6397
+ const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
6246
6398
  const normalizedPath = normalizePath(filepath);
6247
6399
  const dataFields = await this.formatBodyOnPayload(filepath, data);
6248
6400
  const collection = await this.collectionForPath(filepath);
@@ -6290,6 +6442,14 @@ var Database = class {
6290
6442
  let delOps = [];
6291
6443
  if (!isGitKeep(normalizedPath, collection)) {
6292
6444
  putOps = [
6445
+ ...makeRefOpsForDocument(
6446
+ normalizedPath,
6447
+ collectionName,
6448
+ collectionReferences,
6449
+ dataFields,
6450
+ "put",
6451
+ level
6452
+ ),
6293
6453
  ...makeIndexOpsForDocument(
6294
6454
  normalizedPath,
6295
6455
  collectionName,
@@ -6313,6 +6473,14 @@ var Database = class {
6313
6473
  SUBLEVEL_OPTIONS
6314
6474
  ).get(normalizedPath);
6315
6475
  delOps = existingItem ? [
6476
+ ...makeRefOpsForDocument(
6477
+ normalizedPath,
6478
+ collectionName,
6479
+ collectionReferences,
6480
+ existingItem,
6481
+ "del",
6482
+ level
6483
+ ),
6316
6484
  ...makeIndexOpsForDocument(
6317
6485
  normalizedPath,
6318
6486
  collectionName,
@@ -6355,8 +6523,7 @@ var Database = class {
6355
6523
  throw new TinaFetchError(`Error in PUT for ${filepath}`, {
6356
6524
  originalError: error,
6357
6525
  file: filepath,
6358
- collection: collectionName,
6359
- stack: error.stack
6526
+ collection: collectionName
6360
6527
  });
6361
6528
  }
6362
6529
  };
@@ -6475,6 +6642,22 @@ var Database = class {
6475
6642
  this.tinaSchema = await createSchema({ schema });
6476
6643
  return this.tinaSchema;
6477
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
+ };
6478
6661
  this.getIndexDefinitions = async (level) => {
6479
6662
  if (!this.collectionIndexDefinitions) {
6480
6663
  await new Promise(async (resolve2, reject) => {
@@ -6484,11 +6667,53 @@ var Database = class {
6484
6667
  const collections = schema.getCollections();
6485
6668
  for (const collection of collections) {
6486
6669
  const indexDefinitions = {
6487
- [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] }
6670
+ [DEFAULT_COLLECTION_SORT_KEY]: { fields: [] },
6488
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
+ }
6489
6687
  };
6490
- if (collection.fields) {
6491
- 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) {
6492
6717
  if (field.indexed !== void 0 && field.indexed === false || field.type === "object") {
6493
6718
  continue;
6494
6719
  }
@@ -6636,29 +6861,35 @@ var Database = class {
6636
6861
  }
6637
6862
  startKey = startKey || key || "";
6638
6863
  endKey = key || "";
6639
- edges = [...edges, { cursor: key, path: filepath }];
6864
+ edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
6640
6865
  }
6641
6866
  return {
6642
- edges: await sequential(edges, async (edge) => {
6643
- try {
6644
- const node = await hydrator(edge.path);
6645
- return {
6646
- node,
6647
- cursor: btoa(edge.cursor)
6648
- };
6649
- } catch (error) {
6650
- console.log(error);
6651
- if (error instanceof Error && (!edge.path.includes(".tina/__generated__/_graphql.json") || !edge.path.includes("tina/__generated__/_graphql.json"))) {
6652
- throw new TinaQueryError({
6653
- originalError: error,
6654
- file: edge.path,
6655
- collection: collection.name,
6656
- stack: error.stack
6657
- });
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;
6658
6890
  }
6659
- throw error;
6660
6891
  }
6661
- }),
6892
+ ),
6662
6893
  pageInfo: {
6663
6894
  hasPreviousPage,
6664
6895
  hasNextPage,
@@ -6782,13 +7013,14 @@ var Database = class {
6782
7013
  documentPaths,
6783
7014
  async (collection, documentPaths2) => {
6784
7015
  if (collection && !collection.isDetached) {
6785
- await _indexContent(
6786
- this,
6787
- this.contentLevel,
6788
- documentPaths2,
7016
+ await _indexContent({
7017
+ database: this,
7018
+ level: this.contentLevel,
7019
+ documentPaths: documentPaths2,
6789
7020
  enqueueOps,
6790
- collection
6791
- );
7021
+ collection,
7022
+ isPartialReindex: true
7023
+ });
6792
7024
  }
6793
7025
  }
6794
7026
  );
@@ -6804,6 +7036,7 @@ var Database = class {
6804
7036
  throw new Error(`No collection found for path: ${filepath}`);
6805
7037
  }
6806
7038
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7039
+ const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
6807
7040
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
6808
7041
  let level = this.contentLevel;
6809
7042
  if (collection?.isDetached) {
@@ -6822,6 +7055,14 @@ var Database = class {
6822
7055
  collection.path || ""
6823
7056
  );
6824
7057
  await this.contentLevel.batch([
7058
+ ...makeRefOpsForDocument(
7059
+ normalizedPath,
7060
+ collection.name,
7061
+ collectionReferences,
7062
+ item,
7063
+ "del",
7064
+ level
7065
+ ),
6825
7066
  ...makeIndexOpsForDocument(
6826
7067
  normalizedPath,
6827
7068
  collection.name,
@@ -6886,20 +7127,26 @@ var Database = class {
6886
7127
  );
6887
7128
  const doc = await level2.keys({ limit: 1 }).next();
6888
7129
  if (!doc) {
6889
- await _indexContent(
6890
- this,
6891
- level2,
6892
- contentPaths,
7130
+ await _indexContent({
7131
+ database: this,
7132
+ level: level2,
7133
+ documentPaths: contentPaths,
6893
7134
  enqueueOps,
6894
7135
  collection,
6895
- userFields.map((field) => [
7136
+ passwordFields: userFields.map((field) => [
6896
7137
  ...field.path,
6897
7138
  field.passwordFieldName
6898
7139
  ])
6899
- );
7140
+ });
6900
7141
  }
6901
7142
  } else {
6902
- await _indexContent(this, level, contentPaths, enqueueOps, collection);
7143
+ await _indexContent({
7144
+ database: this,
7145
+ level,
7146
+ documentPaths: contentPaths,
7147
+ enqueueOps,
7148
+ collection
7149
+ });
6903
7150
  }
6904
7151
  }
6905
7152
  );
@@ -7038,7 +7285,15 @@ var hashPasswordValues = async (data, passwordFields) => Promise.all(
7038
7285
  )
7039
7286
  );
7040
7287
  var isGitKeep = (filepath, collection) => filepath.endsWith(`.gitkeep.${collection?.format || "md"}`);
7041
- 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
+ }) => {
7042
7297
  let collectionIndexDefinitions;
7043
7298
  let collectionPath;
7044
7299
  if (collection) {
@@ -7049,6 +7304,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7049
7304
  }
7050
7305
  collectionPath = collection.path;
7051
7306
  }
7307
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
7052
7308
  const tinaSchema = await database.getSchema();
7053
7309
  let templateInfo = null;
7054
7310
  if (collection) {
@@ -7078,35 +7334,53 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7078
7334
  normalizedPath,
7079
7335
  collectionPath || ""
7080
7336
  );
7081
- const item = await rootSublevel.get(normalizedPath);
7082
- if (item) {
7083
- await database.contentLevel.batch([
7084
- ...makeIndexOpsForDocument(
7085
- normalizedPath,
7086
- collection.name,
7087
- collectionIndexDefinitions,
7088
- item,
7089
- "del",
7090
- level
7091
- ),
7092
- // folder indices
7093
- ...makeIndexOpsForDocument(
7094
- normalizedPath,
7095
- `${collection.name}_${folderKey}`,
7096
- collectionIndexDefinitions,
7097
- item,
7098
- "del",
7099
- level
7100
- ),
7101
- {
7102
- type: "del",
7103
- key: normalizedPath,
7104
- sublevel: rootSublevel
7105
- }
7106
- ]);
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
+ }
7107
7373
  }
7108
7374
  if (!isGitKeep(filepath, collection)) {
7109
7375
  await enqueueOps([
7376
+ ...makeRefOpsForDocument(
7377
+ normalizedPath,
7378
+ collection?.name,
7379
+ collectionReferences,
7380
+ aliasedData,
7381
+ "put",
7382
+ level
7383
+ ),
7110
7384
  ...makeIndexOpsForDocument(
7111
7385
  normalizedPath,
7112
7386
  collection?.name,
@@ -7139,8 +7413,7 @@ var _indexContent = async (database, level, documentPaths, enqueueOps, collectio
7139
7413
  throw new TinaFetchError(`Unable to seed ${filepath}`, {
7140
7414
  originalError: error,
7141
7415
  file: filepath,
7142
- collection: collection?.name,
7143
- stack: error.stack
7416
+ collection: collection?.name
7144
7417
  });
7145
7418
  }
7146
7419
  });
@@ -7170,6 +7443,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7170
7443
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
7171
7444
  }
7172
7445
  }
7446
+ const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
7173
7447
  const tinaSchema = await database.getSchema();
7174
7448
  let templateInfo = null;
7175
7449
  if (collection) {
@@ -7193,6 +7467,14 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
7193
7467
  item
7194
7468
  ) : item;
7195
7469
  await enqueueOps([
7470
+ ...makeRefOpsForDocument(
7471
+ itemKey,
7472
+ collection?.name,
7473
+ collectionReferences,
7474
+ aliasedData,
7475
+ "del",
7476
+ database.contentLevel
7477
+ ),
7196
7478
  ...makeIndexOpsForDocument(
7197
7479
  itemKey,
7198
7480
  collection.name,
@@ -7322,8 +7604,8 @@ import path6 from "path";
7322
7604
  import normalize from "normalize-path";
7323
7605
  var FilesystemBridge = class {
7324
7606
  constructor(rootPath, outputPath) {
7325
- this.rootPath = rootPath || "";
7326
- this.outputPath = outputPath || rootPath;
7607
+ this.rootPath = path6.resolve(rootPath);
7608
+ this.outputPath = outputPath ? path6.resolve(outputPath) : this.rootPath;
7327
7609
  }
7328
7610
  async glob(pattern, extension) {
7329
7611
  const basePath = path6.join(this.outputPath, ...pattern.split("/"));
@@ -7335,19 +7617,19 @@ var FilesystemBridge = class {
7335
7617
  }
7336
7618
  );
7337
7619
  const posixRootPath = normalize(this.outputPath);
7338
- return items.map((item) => {
7339
- return item.replace(posixRootPath, "").replace(/^\/|\/$/g, "");
7340
- });
7620
+ return items.map(
7621
+ (item) => item.substring(posixRootPath.length).replace(/^\/|\/$/g, "")
7622
+ );
7341
7623
  }
7342
7624
  async delete(filepath) {
7343
7625
  await fs2.remove(path6.join(this.outputPath, filepath));
7344
7626
  }
7345
7627
  async get(filepath) {
7346
- return fs2.readFileSync(path6.join(this.outputPath, filepath)).toString();
7628
+ return (await fs2.readFile(path6.join(this.outputPath, filepath))).toString();
7347
7629
  }
7348
7630
  async put(filepath, data, basePathOverride) {
7349
7631
  const basePath = basePathOverride || this.outputPath;
7350
- await fs2.outputFileSync(path6.join(basePath, filepath), data);
7632
+ await fs2.outputFile(path6.join(basePath, filepath), data);
7351
7633
  }
7352
7634
  };
7353
7635
  var AuditFileSystemBridge = class extends FilesystemBridge {