@slicemachine/manager 0.24.14-alpha.jp-update-cr-links-unit-more.6 → 0.24.14-alpha.jp-update-cr-links-only-when-changed.3

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.
@@ -188,6 +188,151 @@ export class CustomTypesManager extends BaseManager {
188
188
  };
189
189
  }
190
190
 
191
+ private updateCRCustomType(
192
+ args: { customType: CrCustomType } & CustomTypeFieldIdChangedMeta,
193
+ ): CrCustomType {
194
+ const [previousCustomTypeId, previousFieldId] = args.previousPath;
195
+ const [newCustomTypeId, newFieldId] = args.newPath;
196
+
197
+ if (!previousCustomTypeId || !newCustomTypeId) {
198
+ throw new Error(
199
+ "Could not find a customtype id in previousPath and/or newPath, which should not be possible.",
200
+ );
201
+ }
202
+
203
+ if (!previousFieldId || !newFieldId) {
204
+ throw new Error(
205
+ "Could not find a field id in previousPath and/or newPath, which should not be possible.",
206
+ );
207
+ }
208
+
209
+ const customType = shallowCloneIfObject(args.customType);
210
+
211
+ if (typeof customType === "string" || !customType.fields) {
212
+ return customType;
213
+ }
214
+
215
+ const matchedCustomTypeId = customType.id === previousCustomTypeId;
216
+
217
+ const newFields = customType.fields.map((fieldArg) => {
218
+ const customTypeField = shallowCloneIfObject(fieldArg);
219
+
220
+ if (typeof customTypeField === "string") {
221
+ if (
222
+ matchedCustomTypeId &&
223
+ customTypeField === previousFieldId &&
224
+ customTypeField !== newFieldId
225
+ ) {
226
+ // We have reached a field id that matches the id that was renamed,
227
+ // so we update it new one. The field is a string, so return the new
228
+ // id.
229
+ return newFieldId;
230
+ }
231
+
232
+ return customTypeField;
233
+ }
234
+
235
+ if (
236
+ matchedCustomTypeId &&
237
+ customTypeField.id === previousFieldId &&
238
+ customTypeField.id !== newFieldId
239
+ ) {
240
+ // We have reached a field id that matches the id that was renamed,
241
+ // so we update it new one.
242
+ // Since field is not a string, we don't exit, as we might have
243
+ // something to update further down in customtypes.
244
+ customTypeField.id = newFieldId;
245
+ }
246
+
247
+ return {
248
+ ...customTypeField,
249
+ customtypes: customTypeField.customtypes.map((customTypeArg) => {
250
+ const nestedCustomType = shallowCloneIfObject(customTypeArg);
251
+
252
+ if (
253
+ typeof nestedCustomType === "string" ||
254
+ !nestedCustomType.fields ||
255
+ // Since we are on the last level, if we don't start matching right
256
+ // at the custom type id, we can return exit early because it's not
257
+ // a match.
258
+ nestedCustomType.id !== previousCustomTypeId
259
+ ) {
260
+ return nestedCustomType;
261
+ }
262
+
263
+ return {
264
+ ...nestedCustomType,
265
+ fields: nestedCustomType.fields.map((fieldArg) => {
266
+ const nestedCustomTypeField = shallowCloneIfObject(fieldArg);
267
+
268
+ if (
269
+ nestedCustomTypeField === previousFieldId &&
270
+ nestedCustomTypeField !== newFieldId
271
+ ) {
272
+ // Matches the previous id, so we update it and return because
273
+ // it's the last level.
274
+ return newFieldId;
275
+ }
276
+
277
+ return nestedCustomTypeField;
278
+ }),
279
+ };
280
+ }),
281
+ };
282
+ });
283
+
284
+ return { ...customType, fields: newFields };
285
+ }
286
+
287
+ /**
288
+ * Map over the custom types of a Content Relationship Link and update the API
289
+ * IDs that were changed during the custom type update.
290
+ */
291
+ private updateCRCustomTypes(
292
+ args: { customTypes: CrCustomTypes } & CustomTypeFieldIdChangedMeta,
293
+ ): CrCustomTypes {
294
+ const { customTypes, ...updateMeta } = args;
295
+
296
+ return customTypes.map((customType) => {
297
+ return this.updateCRCustomType({ customType, ...updateMeta });
298
+ });
299
+ }
300
+
301
+ /**
302
+ * Update the Content Relationship API IDs of a single field. The change is
303
+ * determined by the `previousPath` and `newPath` properties.
304
+ */
305
+ private updateFieldContentRelationships<
306
+ T extends UID | NestableWidget | Group | NestedGroup,
307
+ >(
308
+ args: { field: T } & CustomTypeFieldIdChangedMeta,
309
+ ): { field: T; changed: boolean } {
310
+ const { field, ...updateMeta } = args;
311
+ if (
312
+ field.type !== "Link" ||
313
+ field.config?.select !== "document" ||
314
+ !field.config?.customtypes
315
+ ) {
316
+ // not a content relationship field
317
+ return { field, changed: false };
318
+ }
319
+
320
+ const newField = {
321
+ ...field,
322
+ config: {
323
+ ...field.config,
324
+ customtypes: this.updateCRCustomTypes({
325
+ ...updateMeta,
326
+ customTypes: field.config.customtypes,
327
+ }),
328
+ },
329
+ };
330
+
331
+ const changed = JSON.stringify(field) !== JSON.stringify(newField);
332
+
333
+ return { field: newField, changed };
334
+ }
335
+
191
336
  /**
192
337
  * Update the Content Relationship API IDs for all existing custom types and
193
338
  * slices. The change is determined by properties inside the `updateMeta`
@@ -212,20 +357,34 @@ export class CustomTypesManager extends BaseManager {
212
357
  // Find existing content relationships that link to the renamed field id in
213
358
  // any custom type and update them to use the new one.
214
359
  const customTypes = await this.readAllCustomTypes();
360
+ for (const customType of customTypes.models) {
361
+ // Keep track of whether the model has changed to avoid calling the
362
+ // update hook if nothing has changed
363
+ let customTypeHasChanged = false;
364
+
365
+ const updatedCustomTypeModel = traverseCustomType({
366
+ customType: customType.model,
367
+ onField: ({ field }) => {
368
+ const update = this.updateFieldContentRelationships({
369
+ field,
370
+ previousPath,
371
+ newPath,
372
+ });
373
+
374
+ customTypeHasChanged ||= update.changed;
375
+
376
+ return update.field;
377
+ },
378
+ });
215
379
 
216
- updateCustomTypeContentRelationships({
217
- models: customTypes.models,
218
- onUpdate: (model) => {
219
- pushIfDefined(
220
- crUpdates,
221
- this.sliceMachinePluginRunner?.callHook("custom-type:update", {
222
- model,
380
+ if (customTypeHasChanged) {
381
+ crUpdates.push(
382
+ this.sliceMachinePluginRunner.callHook("custom-type:update", {
383
+ model: updatedCustomTypeModel,
223
384
  }),
224
385
  );
225
- },
226
- previousPath,
227
- newPath,
228
- });
386
+ }
387
+ }
229
388
 
230
389
  // Find existing slice with content relationships that link to the renamed
231
390
  // field id in all libraries and update them to use the new one.
@@ -236,20 +395,36 @@ export class CustomTypesManager extends BaseManager {
236
395
  libraryID: library.libraryID,
237
396
  });
238
397
 
239
- updateSharedSliceContentRelationships({
240
- models: slices.models,
241
- onUpdate: (model) => {
242
- pushIfDefined(
243
- crUpdates,
244
- this.sliceMachinePluginRunner?.callHook("slice:update", {
398
+ for (const slice of slices.models) {
399
+ // Keep track of whether the model has changed to avoid calling the
400
+ // update hook if nothing has changed
401
+ let sliceHasChanged = false;
402
+
403
+ const updatedSliceModel = traverseSharedSlice({
404
+ path: ["."],
405
+ slice: slice.model,
406
+ onField: ({ field }) => {
407
+ const update = this.updateFieldContentRelationships({
408
+ field,
409
+ previousPath,
410
+ newPath,
411
+ });
412
+
413
+ sliceHasChanged ||= update.changed;
414
+
415
+ return update.field;
416
+ },
417
+ });
418
+
419
+ if (sliceHasChanged) {
420
+ crUpdates.push(
421
+ this.sliceMachinePluginRunner.callHook("slice:update", {
245
422
  libraryID: library.libraryID,
246
- model,
423
+ model: updatedSliceModel,
247
424
  }),
248
425
  );
249
- },
250
- previousPath,
251
- newPath,
252
- });
426
+ }
427
+ }
253
428
  }
254
429
 
255
430
  // Process all the Content Relationship updates at once.
@@ -495,193 +670,6 @@ const InferSliceResponse = z.object({
495
670
  langSmithUrl: z.string().url().optional(),
496
671
  });
497
672
 
498
- function updateCRCustomType(
499
- args: { customType: CrCustomType } & CustomTypeFieldIdChangedMeta,
500
- ): CrCustomType {
501
- const [previousCustomTypeId, previousFieldId] = args.previousPath;
502
- const [newCustomTypeId, newFieldId] = args.newPath;
503
-
504
- if (!previousCustomTypeId || !newCustomTypeId) {
505
- throw new Error(
506
- "Could not find a customtype id in previousPath and/or newPath, which should not be possible.",
507
- );
508
- }
509
-
510
- if (!previousFieldId || !newFieldId) {
511
- throw new Error(
512
- "Could not find a field id in previousPath and/or newPath, which should not be possible.",
513
- );
514
- }
515
-
516
- const customType = shallowCloneIfObject(args.customType);
517
-
518
- if (typeof customType === "string" || !customType.fields) {
519
- return customType;
520
- }
521
-
522
- const matchedCustomTypeId = customType.id === previousCustomTypeId;
523
-
524
- const newFields = customType.fields.map((fieldArg) => {
525
- const customTypeField = shallowCloneIfObject(fieldArg);
526
-
527
- if (typeof customTypeField === "string") {
528
- if (
529
- matchedCustomTypeId &&
530
- customTypeField === previousFieldId &&
531
- customTypeField !== newFieldId
532
- ) {
533
- // We have reached a field id that matches the id that was renamed,
534
- // so we update it new one. The field is a string, so return the new
535
- // id.
536
- return newFieldId;
537
- }
538
-
539
- return customTypeField;
540
- }
541
-
542
- if (
543
- matchedCustomTypeId &&
544
- customTypeField.id === previousFieldId &&
545
- customTypeField.id !== newFieldId
546
- ) {
547
- // We have reached a field id that matches the id that was renamed,
548
- // so we update it new one.
549
- // Since field is not a string, we don't exit, as we might have
550
- // something to update further down in customtypes.
551
- customTypeField.id = newFieldId;
552
- }
553
-
554
- return {
555
- ...customTypeField,
556
- customtypes: customTypeField.customtypes.map((customTypeArg) => {
557
- const nestedCustomType = shallowCloneIfObject(customTypeArg);
558
-
559
- if (
560
- typeof nestedCustomType === "string" ||
561
- !nestedCustomType.fields ||
562
- // Since we are on the last level, if we don't start matching right
563
- // at the custom type id, we can return exit early because it's not
564
- // a match.
565
- nestedCustomType.id !== previousCustomTypeId
566
- ) {
567
- return nestedCustomType;
568
- }
569
-
570
- return {
571
- ...nestedCustomType,
572
- fields: nestedCustomType.fields.map((fieldArg) => {
573
- const nestedCustomTypeField = shallowCloneIfObject(fieldArg);
574
-
575
- if (
576
- nestedCustomTypeField === previousFieldId &&
577
- nestedCustomTypeField !== newFieldId
578
- ) {
579
- // Matches the previous id, so we update it and return because
580
- // it's the last level.
581
- return newFieldId;
582
- }
583
-
584
- return nestedCustomTypeField;
585
- }),
586
- };
587
- }),
588
- };
589
- });
590
-
591
- return { ...customType, fields: newFields };
592
- }
593
-
594
- /**
595
- * Map over the custom types of a Content Relationship Link and update the API
596
- * IDs that were changed during the custom type update.
597
- */
598
- function updateCRCustomTypes(
599
- args: { customTypes: CrCustomTypes } & CustomTypeFieldIdChangedMeta,
600
- ): CrCustomTypes {
601
- const { customTypes, ...updateMeta } = args;
602
-
603
- return customTypes.map((customType) => {
604
- return updateCRCustomType({ customType, ...updateMeta });
605
- });
606
- }
607
-
608
- /**
609
- * Update the Content Relationship API IDs of a single field. The change is
610
- * determined by the `previousPath` and `newPath` properties.
611
- */
612
- function updateFieldContentRelationships<
613
- T extends UID | NestableWidget | Group | NestedGroup,
614
- >(args: { field: T } & CustomTypeFieldIdChangedMeta): T {
615
- const { field, ...updateMeta } = args;
616
- if (
617
- field.type !== "Link" ||
618
- field.config?.select !== "document" ||
619
- !field.config?.customtypes
620
- ) {
621
- // not a content relationship field
622
- return field;
623
- }
624
-
625
- const newCustomTypes = updateCRCustomTypes({
626
- ...updateMeta,
627
- customTypes: field.config.customtypes,
628
- });
629
-
630
- return {
631
- ...field,
632
- config: { ...field.config, customtypes: newCustomTypes },
633
- };
634
- }
635
-
636
- export function updateCustomTypeContentRelationships(
637
- args: {
638
- models: { model: CustomType }[];
639
- onUpdate: (model: CustomType) => void;
640
- } & CustomTypeFieldIdChangedMeta,
641
- ): void {
642
- const { models, previousPath, newPath, onUpdate } = args;
643
-
644
- for (const customType of models) {
645
- const updatedCustomTypeModel = traverseCustomType({
646
- customType: customType.model,
647
- onField: ({ field }) => {
648
- return updateFieldContentRelationships({
649
- field,
650
- previousPath,
651
- newPath,
652
- });
653
- },
654
- });
655
-
656
- onUpdate(updatedCustomTypeModel);
657
- }
658
- }
659
-
660
- export function updateSharedSliceContentRelationships(
661
- args: {
662
- models: { model: SharedSlice }[];
663
- onUpdate: (model: SharedSlice) => void;
664
- } & CustomTypeFieldIdChangedMeta,
665
- ): void {
666
- const { models, previousPath, newPath, onUpdate } = args;
667
-
668
- for (const slice of models) {
669
- const updatedSliceModel = traverseSharedSlice({
670
- path: ["."],
671
- slice: slice.model,
672
- onField: ({ field }) => {
673
- return updateFieldContentRelationships({
674
- field,
675
- previousPath,
676
- newPath,
677
- });
678
- },
679
- });
680
-
681
- onUpdate(updatedSliceModel);
682
- }
683
- }
684
-
685
673
  function shallowCloneIfObject<T>(value: T): T {
686
674
  if (typeof value === "object") {
687
675
  return { ...value };
@@ -689,9 +677,3 @@ function shallowCloneIfObject<T>(value: T): T {
689
677
 
690
678
  return value;
691
679
  }
692
-
693
- function pushIfDefined<T>(array: T[], value: T | undefined) {
694
- if (value) {
695
- array.push(value);
696
- }
697
- }