@verdant-web/store 3.4.0 → 3.5.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.
@@ -549,21 +549,23 @@ export class Entity<
549
549
  );
550
550
  }
551
551
  const child = view[key as any];
552
- const schema = getChildFieldSchema(this.schema, key);
553
- if (!schema) {
552
+ const fieldSchema = getChildFieldSchema(this.schema, key);
553
+ if (!fieldSchema) {
554
554
  throw new Error(
555
555
  `No schema for key ${String(key)} in ${JSON.stringify(this.schema)}`,
556
556
  );
557
557
  }
558
558
  if (isRef(child)) {
559
559
  if (isFileRef(child)) {
560
- if (schema.type !== 'file') {
560
+ if (fieldSchema.type !== 'file') {
561
561
  throw new Error(
562
- `Expected file schema for key ${String(key)}, got ${schema.type}`,
562
+ `Expected file schema for key ${String(key)}, got ${
563
+ fieldSchema.type
564
+ }`,
563
565
  );
564
566
  }
565
567
  const file = this.files.get(child.id, {
566
- downloadRemote: !!schema.downloadRemote,
568
+ downloadRemote: !!fieldSchema.downloadRemote,
567
569
  });
568
570
 
569
571
  // FIXME: this seems bad and inconsistent
@@ -576,20 +578,25 @@ export class Entity<
576
578
  return this.getChild(key, child.id) as KeyValue[Key];
577
579
  }
578
580
  } else {
581
+ // if this is a Map type, a missing child is
582
+ // just an empty spot
583
+ if (this.schema.type === 'map' && child === undefined) {
584
+ return undefined as KeyValue[Key];
585
+ }
579
586
  // prune invalid primitive fields
580
587
  if (
581
588
  validateEntityField({
582
- field: schema,
589
+ field: fieldSchema,
583
590
  value: child,
584
591
  fieldPath: [...this.fieldPath, key],
585
592
  depth: 1,
586
593
  requireDefaults: true,
587
594
  })
588
595
  ) {
589
- if (hasDefault(schema)) {
590
- return getDefault(schema);
596
+ if (hasDefault(fieldSchema)) {
597
+ return getDefault(fieldSchema);
591
598
  }
592
- if (isNullable(schema)) {
599
+ if (isNullable(fieldSchema)) {
593
600
  return null as any;
594
601
  }
595
602
  return undefined as any;
@@ -598,6 +605,30 @@ export class Entity<
598
605
  }
599
606
  };
600
607
 
608
+ /**
609
+ * Gets a value on this entity. If the value is not
610
+ * present, it will be set to the provided default
611
+ * and returned synchronously. This method only sets
612
+ * a new value once when a field is empty; subsequent
613
+ * calls will retrieve the created value until it is
614
+ * deleted.
615
+ *
616
+ * Note that this should only be called for nullable
617
+ * fields. If the field is not nullable, the existing
618
+ * value or the default value will always be returned,
619
+ * and the default will not be set.
620
+ */
621
+ getOrSet = <Key extends keyof Init & keyof KeyValue>(
622
+ key: Key,
623
+ init: Init[Key],
624
+ ): KeyValue[Key] => {
625
+ assertNotSymbol(key);
626
+ const existing = this.get(key);
627
+ if (existing) return existing;
628
+ this.set(key as any, init);
629
+ return this.get(key);
630
+ };
631
+
601
632
  private processInputValue = (value: any, key: any) => {
602
633
  if (this.readonlyKeys.includes(key as string)) {
603
634
  throw new Error(`Cannot set readonly key ${key.toString()}`);
@@ -755,7 +786,12 @@ export class Entity<
755
786
  {
756
787
  merge = true,
757
788
  replaceSubObjects = false,
758
- }: { replaceSubObjects?: boolean; merge?: boolean } = {},
789
+ preserveUndefined = false,
790
+ }: {
791
+ replaceSubObjects?: boolean;
792
+ merge?: boolean;
793
+ preserveUndefined?: boolean;
794
+ } = {},
759
795
  ): void => {
760
796
  if (!merge && this.schema.type !== 'any' && this.schema.type !== 'map') {
761
797
  throw new Error(
@@ -768,6 +804,9 @@ export class Entity<
768
804
  if (this.readonlyKeys.includes(key as any)) {
769
805
  throw new Error(`Cannot set readonly key ${key.toString()}`);
770
806
  }
807
+ // ignore undefined values unless overridden
808
+ if (field === undefined && !preserveUndefined) continue;
809
+
771
810
  const fieldSchema = getChildFieldSchema(this.schema, key);
772
811
  if (fieldSchema) {
773
812
  traverseCollectionFieldsAndApplyDefaults(field, fieldSchema);