@tinacms/graphql 4.0.0 → 4.0.1

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  */
4
4
  import { BatchOp, Level } from './level';
5
- import type { Collection } from '@tinacms/schema-tools';
5
+ import type { Collection, TinaSchema } from '@tinacms/schema-tools';
6
6
  export declare enum OP {
7
7
  EQ = "eq",
8
8
  GT = "gt",
@@ -77,7 +77,7 @@ export declare class FolderTreeBuilder {
77
77
  }
78
78
  export declare const makeFolderOpsForCollection: <T extends object>(folderTree: FolderTree, collection: Collection<true>, indexDefinitions: Record<string, IndexDefinition>, opType: "put" | "del", level: Level, escapeStr?: StringEscaper) => BatchOp[];
79
79
  export declare const makeIndexOpsForDocument: <T extends object>(filepath: string, collection: string | undefined, indexDefinitions: Record<string, IndexDefinition>, data: T, opType: "put" | "del", level: Level, escapeStr?: StringEscaper) => BatchOp[];
80
- export declare const makeRefOpsForDocument: <T extends object>(filepath: string, collection: string | undefined, references: Record<string, string[]> | undefined | null, data: T, opType: "put" | "del", level: Level) => BatchOp[];
80
+ export declare const makeRefOpsForDocument: <T extends object>(filepath: string, collectionName: string | undefined, tinaSchema: TinaSchema | undefined, data: T, opType: "put" | "del", level: Level) => BatchOp[];
81
81
  export declare const makeStringEscaper: (regex: RegExp, replacement: string) => StringEscaper;
82
82
  export declare const stringEscaper: StringEscaper;
83
83
  export {};
package/dist/index.js CHANGED
@@ -3058,7 +3058,7 @@ var assertCheckboxGroupIsList = (field) => {
3058
3058
  var package_default = {
3059
3059
  name: "@tinacms/graphql",
3060
3060
  type: "module",
3061
- version: "4.0.0",
3061
+ version: "4.0.1",
3062
3062
  main: "dist/index.js",
3063
3063
  module: "./dist/index.js",
3064
3064
  files: [
@@ -3101,7 +3101,7 @@ var package_default = {
3101
3101
  "gray-matter": "catalog:",
3102
3102
  "isomorphic-git": "catalog:",
3103
3103
  "js-sha1": "catalog:",
3104
- "js-yaml": "^3.15.1",
3104
+ "js-yaml": "^3.15.2",
3105
3105
  "jsonpath-plus": "catalog:",
3106
3106
  "many-level": "catalog:",
3107
3107
  micromatch: "catalog:",
@@ -3355,6 +3355,129 @@ import { JSONPath as JSONPath2 } from "jsonpath-plus";
3355
3355
  // src/mdx/index.ts
3356
3356
  import { parseMDX, serializeMDX } from "@tinacms/mdx";
3357
3357
 
3358
+ // src/references/visit.ts
3359
+ var MDX_SEGMENT = { mdx: true };
3360
+ var encodeLocator = (locator) => JSON.stringify(locator);
3361
+ var isRecord = (value) => typeof value === "object" && value !== null;
3362
+ var visitReferences = (data, collectable, tinaSchema, visit) => {
3363
+ const visitCollectable = (collectableHere, value, locator) => {
3364
+ if (!isRecord(value)) {
3365
+ return false;
3366
+ }
3367
+ let fields;
3368
+ try {
3369
+ fields = tinaSchema.getTemplateForData({
3370
+ data: value,
3371
+ collection: collectableHere
3372
+ })?.fields;
3373
+ } catch {
3374
+ return false;
3375
+ }
3376
+ let replaced = false;
3377
+ for (const field of fields ?? []) {
3378
+ replaced = visitField(field, value, locator) || replaced;
3379
+ }
3380
+ return replaced;
3381
+ };
3382
+ const visitField = (field, parent, locator) => {
3383
+ const key = field.type === "rich-text" && field.isBody && "$_body" in parent ? "$_body" : field.name;
3384
+ const value = parent[key];
3385
+ if (value === void 0 || value === null) {
3386
+ return false;
3387
+ }
3388
+ const at = [...locator, field.name];
3389
+ let replaced = false;
3390
+ if (field.type === "reference") {
3391
+ if (field.list && Array.isArray(value)) {
3392
+ for (const [index, item] of value.entries()) {
3393
+ if (typeof item !== "string" || item.length === 0) {
3394
+ continue;
3395
+ }
3396
+ visit({
3397
+ value: item,
3398
+ collections: field.collections ?? [],
3399
+ locator: [...at, index],
3400
+ replace: (next) => {
3401
+ value[index] = next;
3402
+ replaced = true;
3403
+ }
3404
+ });
3405
+ }
3406
+ return replaced;
3407
+ } else if (typeof value !== "string" || value.length === 0) {
3408
+ return false;
3409
+ }
3410
+ visit({
3411
+ value,
3412
+ collections: field.collections ?? [],
3413
+ locator: at,
3414
+ replace: (next) => {
3415
+ parent[field.name] = next;
3416
+ replaced = true;
3417
+ }
3418
+ });
3419
+ return replaced;
3420
+ } else if (field.type === "object") {
3421
+ if (field.list && Array.isArray(value)) {
3422
+ for (const [index, item] of value.entries()) {
3423
+ replaced = visitCollectable(field, item, [...at, index]) || replaced;
3424
+ }
3425
+ return replaced;
3426
+ }
3427
+ return visitCollectable(field, value, at);
3428
+ } else if (field.type === "rich-text") {
3429
+ return visitRichText(field, parent, key, at);
3430
+ }
3431
+ return false;
3432
+ };
3433
+ const visitRichText = (field, parent, key, at) => {
3434
+ const stored = parent[key];
3435
+ const storedIsText = typeof stored === "string";
3436
+ if (!storedIsText && !isRecord(stored)) {
3437
+ return false;
3438
+ }
3439
+ let tree;
3440
+ if (storedIsText) {
3441
+ try {
3442
+ tree = parseMDX(stored, field, (value) => value);
3443
+ } catch {
3444
+ return false;
3445
+ }
3446
+ } else {
3447
+ tree = stored;
3448
+ }
3449
+ const replaced = visitNodes2(tree?.children, field, [...at, MDX_SEGMENT]);
3450
+ if (replaced && storedIsText) {
3451
+ parent[key] = serializeMDX(tree, field, (value) => value);
3452
+ }
3453
+ return replaced;
3454
+ };
3455
+ const visitNodes2 = (nodes, field, at) => {
3456
+ if (!Array.isArray(nodes)) {
3457
+ return false;
3458
+ }
3459
+ const templates = field.templates ?? [];
3460
+ let replaced = false;
3461
+ for (const [index, node] of nodes.entries()) {
3462
+ if (!isRecord(node)) {
3463
+ continue;
3464
+ }
3465
+ const here = [...at, index];
3466
+ if (typeof node.name === "string" && (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && isRecord(node.props)) {
3467
+ const template = templates.find(
3468
+ (candidate) => lastItem(candidate.namespace) === node.name
3469
+ );
3470
+ for (const templateField of template?.fields ?? []) {
3471
+ replaced = visitField(templateField, node.props, [...here, "props"]) || replaced;
3472
+ }
3473
+ }
3474
+ replaced = visitNodes2(node.children, field, [...here, "children"]) || replaced;
3475
+ }
3476
+ return replaced;
3477
+ };
3478
+ return visitCollectable(collectable, data, []);
3479
+ };
3480
+
3358
3481
  // src/resolver/index.ts
3359
3482
  import {
3360
3483
  ERR_ALREADY_EXISTS,
@@ -4431,54 +4554,41 @@ var makeIndexOpsForDocument = (filepath, collection, indexDefinitions, data, opT
4431
4554
  }
4432
4555
  return result;
4433
4556
  };
4434
- var makeRefOpsForDocument = (filepath, collection, references, data, opType, level) => {
4557
+ var makeRefOpsForDocument = (filepath, collectionName, tinaSchema, data, opType, level) => {
4435
4558
  const result = [];
4436
- if (collection) {
4437
- for (const [c, referencePaths] of Object.entries(references || {})) {
4438
- if (!referencePaths.length) {
4439
- continue;
4440
- }
4441
- const collectionSublevel = level.sublevel(c, SUBLEVEL_OPTIONS);
4442
- const refSublevel = collectionSublevel.sublevel(
4443
- REFS_COLLECTIONS_SORT_KEY,
4444
- SUBLEVEL_OPTIONS
4445
- );
4446
- const references2 = {};
4447
- for (const path9 of referencePaths) {
4448
- const ref = JSONPath({ path: path9, json: data });
4449
- if (!ref) {
4450
- continue;
4451
- }
4452
- if (Array.isArray(ref)) {
4453
- for (const r of ref) {
4454
- if (!r) {
4455
- continue;
4456
- }
4457
- if (references2[r]) {
4458
- references2[r].push(path9);
4459
- } else {
4460
- references2[r] = [path9];
4461
- }
4462
- }
4463
- } else {
4464
- if (references2[ref]) {
4465
- references2[ref].push(path9);
4466
- } else {
4467
- references2[ref] = [path9];
4468
- }
4469
- }
4470
- }
4471
- for (const ref of Object.keys(references2)) {
4472
- for (const path9 of references2[ref]) {
4473
- result.push({
4474
- type: opType,
4475
- key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${path9}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4476
- sublevel: refSublevel,
4477
- value: opType === "put" ? {} : void 0
4478
- });
4479
- }
4559
+ if (!collectionName || !tinaSchema) {
4560
+ return result;
4561
+ }
4562
+ let collection;
4563
+ try {
4564
+ collection = tinaSchema.getCollection(collectionName);
4565
+ } catch {
4566
+ return result;
4567
+ }
4568
+ const byCollection = /* @__PURE__ */ new Map();
4569
+ visitReferences(
4570
+ data,
4571
+ collection,
4572
+ tinaSchema,
4573
+ ({ value, collections, locator }) => {
4574
+ const encoded = encodeLocator(locator);
4575
+ for (const target of collections) {
4576
+ const entries = byCollection.get(target) ?? [];
4577
+ entries.push({ ref: value, locator: encoded });
4578
+ byCollection.set(target, entries);
4480
4579
  }
4481
4580
  }
4581
+ );
4582
+ for (const [target, entries] of byCollection) {
4583
+ const refSublevel = level.sublevel(target, SUBLEVEL_OPTIONS).sublevel(REFS_COLLECTIONS_SORT_KEY, SUBLEVEL_OPTIONS);
4584
+ for (const { ref, locator } of entries) {
4585
+ result.push({
4586
+ type: opType,
4587
+ key: `${ref}${INDEX_KEY_FIELD_SEPARATOR}${locator}${INDEX_KEY_FIELD_SEPARATOR}${filepath}`,
4588
+ sublevel: refSublevel,
4589
+ value: opType === "put" ? {} : void 0
4590
+ });
4591
+ }
4482
4592
  }
4483
4593
  return result;
4484
4594
  };
@@ -5130,34 +5240,6 @@ var transformDocumentIntoPayload = async (fullPath, rawData, tinaSchema, config,
5130
5240
  throw e;
5131
5241
  }
5132
5242
  };
5133
- var updateObjectWithJsonPath = (obj, path9, oldValue, newValue) => {
5134
- let updated = false;
5135
- if (!path9.includes(".") && !path9.includes("[")) {
5136
- if (path9 in obj && obj[path9] === oldValue) {
5137
- obj[path9] = newValue;
5138
- updated = true;
5139
- }
5140
- return { object: obj, updated };
5141
- }
5142
- const parentPath = path9.replace(/\.[^.\[\]]+$/, "");
5143
- const keyToUpdate = path9.match(/[^.\[\]]+$/)[0];
5144
- const parents = JSONPath2({
5145
- path: parentPath,
5146
- json: obj,
5147
- resultType: "value"
5148
- });
5149
- if (parents.length > 0) {
5150
- parents.forEach((parent) => {
5151
- if (parent && typeof parent === "object" && keyToUpdate in parent) {
5152
- if (parent[keyToUpdate] === oldValue) {
5153
- parent[keyToUpdate] = newValue;
5154
- updated = true;
5155
- }
5156
- }
5157
- });
5158
- }
5159
- return { object: obj, updated };
5160
- };
5161
5243
  var Resolver = class _Resolver {
5162
5244
  constructor(init) {
5163
5245
  this.init = init;
@@ -5725,42 +5807,12 @@ var Resolver = class _Resolver {
5725
5807
  await this.assertReferenceUpdates(realPath, collection);
5726
5808
  await this.database.put(newRealPath, doc._rawData, collection.name);
5727
5809
  await this.deleteDocument(realPath);
5728
- const collRefs = await this.findReferences(realPath, collection);
5729
- const holdsLock = this.isAuthCollectionPath(realPath);
5730
- for (const [_collection, docsWithRefs] of Object.entries(collRefs)) {
5731
- for (const [pathToDocWithRef, referencePaths] of Object.entries(
5732
- docsWithRefs
5733
- )) {
5734
- const rewriteReferences = async () => {
5735
- let docWithRef = await this.getRaw(pathToDocWithRef);
5736
- let hasUpdate = false;
5737
- for (const path9 of referencePaths) {
5738
- const { object: object2, updated } = updateObjectWithJsonPath(
5739
- docWithRef,
5740
- path9,
5741
- realPath,
5742
- newRealPath
5743
- );
5744
- docWithRef = object2;
5745
- hasUpdate = updated || hasUpdate;
5746
- }
5747
- if (hasUpdate) {
5748
- const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5749
- if (!collectionWithRef) {
5750
- throw new Error(
5751
- `Unable to find collection for ${pathToDocWithRef}`
5752
- );
5753
- }
5754
- await this.database.put(
5755
- pathToDocWithRef,
5756
- docWithRef,
5757
- collectionWithRef.name
5758
- );
5759
- }
5760
- };
5761
- await (holdsLock ? rewriteReferences() : this.withAuthCollectionLock(pathToDocWithRef, rewriteReferences));
5762
- }
5763
- }
5810
+ await this.rewriteReferences(
5811
+ realPath,
5812
+ newRealPath,
5813
+ collection,
5814
+ this.isAuthCollectionPath(realPath)
5815
+ );
5764
5816
  return this.getDocument(newRealPath);
5765
5817
  }
5766
5818
  if (!newBody) {
@@ -5824,40 +5876,7 @@ var Resolver = class _Resolver {
5824
5876
  const doc = await this.getDocument(realPath);
5825
5877
  await this.deleteDocument(realPath);
5826
5878
  if (hasReferences) {
5827
- const collRefs = await this.findReferences(realPath, collection);
5828
- for (const [_collection, docsWithRefs] of Object.entries(collRefs)) {
5829
- for (const [pathToDocWithRef, referencePaths] of Object.entries(
5830
- docsWithRefs
5831
- )) {
5832
- await this.withAuthCollectionLock(pathToDocWithRef, async () => {
5833
- let refDoc = await this.getRaw(pathToDocWithRef);
5834
- let hasUpdate = false;
5835
- for (const path9 of referencePaths) {
5836
- const { object: object2, updated } = updateObjectWithJsonPath(
5837
- refDoc,
5838
- path9,
5839
- realPath,
5840
- null
5841
- );
5842
- refDoc = object2;
5843
- hasUpdate = updated || hasUpdate;
5844
- }
5845
- if (hasUpdate) {
5846
- const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
5847
- if (!collectionWithRef) {
5848
- throw new Error(
5849
- `Unable to find collection for ${pathToDocWithRef}`
5850
- );
5851
- }
5852
- await this.database.put(
5853
- pathToDocWithRef,
5854
- refDoc,
5855
- collectionWithRef.name
5856
- );
5857
- }
5858
- });
5859
- }
5860
- }
5879
+ await this.rewriteReferences(realPath, null, collection, false);
5861
5880
  }
5862
5881
  return doc;
5863
5882
  };
@@ -6215,6 +6234,47 @@ var Resolver = class _Resolver {
6215
6234
  }
6216
6235
  };
6217
6236
  };
6237
+ /**
6238
+ * Repoints every reference to `from` at `to`, or clears it when `to` is null.
6239
+ *
6240
+ * Each referring document is re-walked rather than addressed by the locators the index stored,
6241
+ * since a stored path cannot address a reference inside rich text. Only a document the walk
6242
+ * changed is written back.
6243
+ */
6244
+ rewriteReferences = async (from, to, collection, holdsLock) => {
6245
+ const collRefs = await this.findReferences(from, collection);
6246
+ for (const docsWithRefs of Object.values(collRefs)) {
6247
+ for (const pathToDocWithRef of Object.keys(docsWithRefs)) {
6248
+ const rewrite = async () => {
6249
+ const collectionWithRef = this.tinaSchema.getCollectionByFullPath(pathToDocWithRef);
6250
+ if (!collectionWithRef) {
6251
+ throw new Error(
6252
+ `Unable to find collection for ${pathToDocWithRef}`
6253
+ );
6254
+ }
6255
+ const docWithRef = await this.getRaw(pathToDocWithRef);
6256
+ const changed = visitReferences(
6257
+ docWithRef,
6258
+ collectionWithRef,
6259
+ this.tinaSchema,
6260
+ (reference) => {
6261
+ if (reference.value === from) {
6262
+ reference.replace(to);
6263
+ }
6264
+ }
6265
+ );
6266
+ if (changed) {
6267
+ await this.database.put(
6268
+ pathToDocWithRef,
6269
+ docWithRef,
6270
+ collectionWithRef.name
6271
+ );
6272
+ }
6273
+ };
6274
+ await (holdsLock ? rewrite() : this.withAuthCollectionLock(pathToDocWithRef, rewrite));
6275
+ }
6276
+ }
6277
+ };
6218
6278
  /**
6219
6279
  * Checks if a document has references to it
6220
6280
  * @param id The id of the document to check for references
@@ -6225,6 +6285,8 @@ var Resolver = class _Resolver {
6225
6285
  let count = 0;
6226
6286
  await this.database.query(
6227
6287
  {
6288
+ // One key answers a boolean; the default page is 50.
6289
+ first: 1,
6228
6290
  collection: c.name,
6229
6291
  filterChain: makeFilterChain({
6230
6292
  conditions: [
@@ -6260,6 +6322,9 @@ var Resolver = class _Resolver {
6260
6322
  const references = {};
6261
6323
  await this.database.query(
6262
6324
  {
6325
+ // Every reference, not the default page of 50, which otherwise caps how many a rename or
6326
+ // delete updates.
6327
+ first: -1,
6263
6328
  collection: c.name,
6264
6329
  filterChain: makeFilterChain({
6265
6330
  conditions: [
@@ -6856,6 +6921,8 @@ var handleCollectionField = (info, args, resolver) => {
6856
6921
  import { ManyLevelGuest } from "many-level";
6857
6922
  import { pipeline } from "readable-stream";
6858
6923
  import { connect } from "net";
6924
+ var CONNECT_RETRIES = 50;
6925
+ var CONNECT_RETRY_DELAY_MS = 100;
6859
6926
  var TinaLevelClient = class extends ManyLevelGuest {
6860
6927
  port;
6861
6928
  _connected = false;
@@ -6865,10 +6932,30 @@ var TinaLevelClient = class extends ManyLevelGuest {
6865
6932
  }
6866
6933
  openConnection() {
6867
6934
  if (this._connected) return;
6868
- const socket = connect(this.port);
6869
- pipeline(socket, this.createRpcStream(), socket, () => {
6870
- this._connected = false;
6871
- });
6935
+ const rpcStream = this.createRpcStream();
6936
+ const tryConnect = (attempt) => {
6937
+ const socket = connect(this.port);
6938
+ const onConnectError = (err) => {
6939
+ socket.destroy();
6940
+ if (attempt < CONNECT_RETRIES) {
6941
+ setTimeout(() => tryConnect(attempt + 1), CONNECT_RETRY_DELAY_MS);
6942
+ } else {
6943
+ console.error(
6944
+ `TinaLevelClient: could not connect to the datalayer server on port ${this.port} after ${attempt + 1} attempts (${err.message}).`
6945
+ );
6946
+ rpcStream.destroy(err);
6947
+ this._connected = false;
6948
+ }
6949
+ };
6950
+ socket.once("error", onConnectError);
6951
+ socket.once("connect", () => {
6952
+ socket.removeListener("error", onConnectError);
6953
+ pipeline(socket, rpcStream, socket, () => {
6954
+ this._connected = false;
6955
+ });
6956
+ });
6957
+ };
6958
+ tryConnect(0);
6872
6959
  this._connected = true;
6873
6960
  }
6874
6961
  };
@@ -7088,7 +7175,7 @@ var Database = class {
7088
7175
  );
7089
7176
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7090
7177
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
7091
- const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
7178
+ const referenceSchema = await this.getSchema();
7092
7179
  const normalizedPath = normalizePath(filepath);
7093
7180
  if (!collection?.isDetached) {
7094
7181
  if (this.bridge) {
@@ -7120,7 +7207,7 @@ var Database = class {
7120
7207
  ...makeRefOpsForDocument(
7121
7208
  normalizedPath,
7122
7209
  collection?.name,
7123
- collectionReferences,
7210
+ referenceSchema,
7124
7211
  dataFields,
7125
7212
  "put",
7126
7213
  level
@@ -7151,7 +7238,7 @@ var Database = class {
7151
7238
  ...makeRefOpsForDocument(
7152
7239
  normalizedPath,
7153
7240
  collection?.name,
7154
- collectionReferences,
7241
+ referenceSchema,
7155
7242
  existingItem,
7156
7243
  "del",
7157
7244
  level
@@ -7203,7 +7290,7 @@ var Database = class {
7203
7290
  );
7204
7291
  collectionIndexDefinitions = indexDefinitions?.[collectionName];
7205
7292
  }
7206
- const collectionReferences = (await this.getCollectionReferences())?.[collectionName];
7293
+ const referenceSchema = await this.getSchema();
7207
7294
  const normalizedPath = normalizePath(filepath);
7208
7295
  const dataFields = await this.formatBodyOnPayload(filepath, data);
7209
7296
  const collection = await this.collectionForPath(filepath);
@@ -7257,7 +7344,7 @@ var Database = class {
7257
7344
  ...makeRefOpsForDocument(
7258
7345
  normalizedPath,
7259
7346
  collectionName,
7260
- collectionReferences,
7347
+ referenceSchema,
7261
7348
  dataFields,
7262
7349
  "put",
7263
7350
  level
@@ -7288,7 +7375,7 @@ var Database = class {
7288
7375
  ...makeRefOpsForDocument(
7289
7376
  normalizedPath,
7290
7377
  collectionName,
7291
- collectionReferences,
7378
+ referenceSchema,
7292
7379
  existingItem,
7293
7380
  "del",
7294
7381
  level
@@ -7714,32 +7801,41 @@ var Database = class {
7714
7801
  endKey = key || "";
7715
7802
  edges = [...edges, { cursor: key, path: filepath, value: itemRecord }];
7716
7803
  }
7717
- return {
7718
- edges: await sequential(
7719
- edges,
7720
- async ({
7721
- cursor,
7722
- path: path9,
7723
- value
7724
- }) => {
7725
- try {
7726
- const node = await hydrator(path9, value);
7727
- return {
7728
- node,
7729
- cursor: btoa(cursor)
7730
- };
7731
- } catch (error) {
7732
- console.log(error);
7733
- if (error instanceof Error && (!path9.includes(".tina/__generated__/_graphql.json") || !path9.includes("tina/__generated__/_graphql.json"))) {
7734
- throw new TinaQueryError({
7735
- originalError: error,
7736
- file: path9,
7737
- collection: collection.name
7738
- });
7739
- }
7740
- throw error;
7804
+ const hydratedEdges = await sequential(
7805
+ edges,
7806
+ async ({
7807
+ cursor,
7808
+ path: path9,
7809
+ value
7810
+ }) => {
7811
+ try {
7812
+ const node = await hydrator(path9, value);
7813
+ return {
7814
+ node,
7815
+ cursor: btoa(cursor)
7816
+ };
7817
+ } catch (error) {
7818
+ if (error instanceof NotFoundError) {
7819
+ console.warn(
7820
+ `Skipping ${path9} in collection ${collection.name}: its index entry has no record. Reindex to remove it.`
7821
+ );
7822
+ return null;
7741
7823
  }
7824
+ console.log(error);
7825
+ if (error instanceof Error && (!path9.includes(".tina/__generated__/_graphql.json") || !path9.includes("tina/__generated__/_graphql.json"))) {
7826
+ throw new TinaQueryError({
7827
+ originalError: error,
7828
+ file: path9,
7829
+ collection: collection.name
7830
+ });
7831
+ }
7832
+ throw error;
7742
7833
  }
7834
+ }
7835
+ );
7836
+ return {
7837
+ edges: hydratedEdges.filter(
7838
+ (edge) => edge !== null
7743
7839
  ),
7744
7840
  pageInfo: {
7745
7841
  hasPreviousPage,
@@ -7905,7 +8001,7 @@ var Database = class {
7905
8001
  throw new Error(`No collection found for path: ${filepath}`);
7906
8002
  }
7907
8003
  const indexDefinitions = await this.getIndexDefinitions(this.contentLevel);
7908
- const collectionReferences = (await this.getCollectionReferences())?.[collection.name];
8004
+ const referenceSchema = await this.getSchema();
7909
8005
  const collectionIndexDefinitions = indexDefinitions?.[collection.name];
7910
8006
  let level = this.contentLevel;
7911
8007
  if (collection?.isDetached) {
@@ -7927,7 +8023,7 @@ var Database = class {
7927
8023
  ...makeRefOpsForDocument(
7928
8024
  normalizedPath,
7929
8025
  collection.name,
7930
- collectionReferences,
8026
+ referenceSchema,
7931
8027
  item,
7932
8028
  "del",
7933
8029
  level
@@ -8074,7 +8170,6 @@ var _indexContent = async ({
8074
8170
  }
8075
8171
  collectionPath = collection.path;
8076
8172
  }
8077
- const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
8078
8173
  const tinaSchema = await database.getSchema();
8079
8174
  let templateInfo = null;
8080
8175
  if (collection) {
@@ -8111,7 +8206,7 @@ var _indexContent = async ({
8111
8206
  ...makeRefOpsForDocument(
8112
8207
  normalizedPath,
8113
8208
  collection?.name,
8114
- collectionReferences,
8209
+ tinaSchema,
8115
8210
  item,
8116
8211
  "del",
8117
8212
  level
@@ -8147,7 +8242,7 @@ var _indexContent = async ({
8147
8242
  ...makeRefOpsForDocument(
8148
8243
  normalizedPath,
8149
8244
  collection?.name,
8150
- collectionReferences,
8245
+ tinaSchema,
8151
8246
  aliasedData,
8152
8247
  "put",
8153
8248
  level
@@ -8217,7 +8312,6 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
8217
8312
  throw new Error(`No indexDefinitions for collection ${collection.name}`);
8218
8313
  }
8219
8314
  }
8220
- const collectionReferences = (await database.getCollectionReferences())?.[collection?.name];
8221
8315
  const tinaSchema = await database.getSchema();
8222
8316
  let templateInfo = null;
8223
8317
  if (collection) {
@@ -8244,7 +8338,7 @@ var _deleteIndexContent = async (database, documentPaths, enqueueOps, collection
8244
8338
  ...makeRefOpsForDocument(
8245
8339
  itemKey,
8246
8340
  collection?.name,
8247
- collectionReferences,
8341
+ tinaSchema,
8248
8342
  aliasedData,
8249
8343
  "del",
8250
8344
  database.contentLevel
@@ -0,0 +1,25 @@
1
+ import type { Collectable, TinaSchema } from '@tinacms/schema-tools';
2
+ /** Marks where a rich-text field was parsed: later segments address tree nodes, not stored keys. */
3
+ export declare const MDX_SEGMENT: {
4
+ readonly mdx: true;
5
+ };
6
+ export type LocatorSegment = string | number | typeof MDX_SEGMENT;
7
+ export type ReferenceVisit = {
8
+ /** The stored value: the path of the document being referenced. */
9
+ value: string;
10
+ /** The collections the field may point at. The index records it under each. */
11
+ collections: string[];
12
+ /** The path to it, as keys and list indices from the document root. */
13
+ locator: LocatorSegment[];
14
+ /** Replaces the value in the document being walked. */
15
+ replace: (next: string | null) => void;
16
+ };
17
+ export declare const encodeLocator: (locator: LocatorSegment[]) => string;
18
+ /**
19
+ * Walks a stored document alongside its schema, visiting every reference it holds, and returns
20
+ * whether any `replace` was called.
21
+ *
22
+ * The index build and the rename and delete rewrites all use this traversal, so they resolve the
23
+ * same references.
24
+ */
25
+ export declare const visitReferences: (data: Record<string, unknown>, collectable: Collectable, tinaSchema: TinaSchema, visit: (reference: ReferenceVisit) => void) => boolean;
@@ -593,6 +593,14 @@ export declare class Resolver {
593
593
  endCursor: string;
594
594
  };
595
595
  }>;
596
+ /**
597
+ * Repoints every reference to `from` at `to`, or clears it when `to` is null.
598
+ *
599
+ * Each referring document is re-walked rather than addressed by the locators the index stored,
600
+ * since a stored path cannot address a reference inside rich text. Only a document the walk
601
+ * changed is written back.
602
+ */
603
+ private rewriteReferences;
596
604
  /**
597
605
  * Checks if a document has references to it
598
606
  * @param id The id of the document to check for references
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tinacms/graphql",
3
3
  "type": "module",
4
- "version": "4.0.0",
4
+ "version": "4.0.1",
5
5
  "main": "dist/index.js",
6
6
  "module": "./dist/index.js",
7
7
  "files": [
@@ -36,15 +36,15 @@
36
36
  "gray-matter": "^4.0.3",
37
37
  "isomorphic-git": "^1.29.0",
38
38
  "js-sha1": "^0.6.0",
39
- "js-yaml": "^3.15.1",
39
+ "js-yaml": "^3.15.2",
40
40
  "jsonpath-plus": "^10.3.0",
41
41
  "many-level": "^2.0.0",
42
42
  "micromatch": "4.0.8",
43
43
  "normalize-path": "^3.0.0",
44
44
  "readable-stream": "^4.7.0",
45
45
  "yup": "^1.6.1",
46
- "@tinacms/schema-tools": "^2.10.1",
47
- "@tinacms/mdx": "^2.3.0"
46
+ "@tinacms/mdx": "^2.4.0",
47
+ "@tinacms/schema-tools": "^2.11.0"
48
48
  },
49
49
  "publishConfig": {
50
50
  "registry": "https://registry.npmjs.org"
@@ -72,8 +72,8 @@
72
72
  "vite": "^4.5.9",
73
73
  "vitest": "^0.32.4",
74
74
  "zod": "^3.24.2",
75
- "@tinacms/schema-tools": "2.10.1",
76
- "@tinacms/scripts": "1.6.4"
75
+ "@tinacms/scripts": "1.6.4",
76
+ "@tinacms/schema-tools": "2.11.0"
77
77
  },
78
78
  "scripts": {
79
79
  "types": "pnpm tsc",