@loro-extended/change 1.0.1 → 1.1.0

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/README.md CHANGED
@@ -497,6 +497,46 @@ const schema = Shape.doc({
497
497
  - `Shape.plain.union(shapes)` - Union of value types (e.g., `string | null`)
498
498
  - `Shape.plain.discriminatedUnion(key, variants)` - Tagged union types with a discriminant key
499
499
 
500
+ #### Nullable Values
501
+
502
+ Use `.nullable()` on value types to create nullable fields with `null` as the default placeholder:
503
+
504
+ ```typescript
505
+ const schema = Shape.doc({
506
+ profile: Shape.struct({
507
+ name: Shape.plain.string().placeholder("Anonymous"),
508
+ email: Shape.plain.string().nullable(), // string | null, defaults to null
509
+ age: Shape.plain.number().nullable(), // number | null, defaults to null
510
+ verified: Shape.plain.boolean().nullable(), // boolean | null, defaults to null
511
+ tags: Shape.plain.array(Shape.plain.string()).nullable(), // string[] | null
512
+ metadata: Shape.plain.record(Shape.plain.string()).nullable(), // Record<string, string> | null
513
+ location: Shape.plain.struct({ // { lat: number, lng: number } | null
514
+ lat: Shape.plain.number(),
515
+ lng: Shape.plain.number(),
516
+ }).nullable(),
517
+ }),
518
+ });
519
+ ```
520
+
521
+ You can chain `.placeholder()` after `.nullable()` to customize the default value:
522
+
523
+ ```typescript
524
+ const schema = Shape.doc({
525
+ settings: Shape.struct({
526
+ // Nullable string with custom default
527
+ nickname: Shape.plain.string().nullable().placeholder("Guest"),
528
+ }),
529
+ });
530
+ ```
531
+
532
+ This is syntactic sugar for the more verbose union pattern:
533
+
534
+ ```typescript
535
+ // These are equivalent:
536
+ email: Shape.plain.string().nullable()
537
+ email: Shape.plain.union([Shape.plain.null(), Shape.plain.string()]).placeholder(null)
538
+ ```
539
+
500
540
  ### TypedDoc API
501
541
 
502
542
  With the proxy-based API, schema properties are accessed directly on the doc object, and meta-operations are accessed via the `$` namespace.
package/dist/index.d.ts CHANGED
@@ -229,6 +229,13 @@ declare class TextRef extends TypedRef<TextContainerShape> {
229
229
  type WithPlaceholder<S extends Shape<any, any, any>> = S & {
230
230
  placeholder(value: S["_placeholder"]): S;
231
231
  };
232
+ /**
233
+ * Type for value shapes that support the .nullable() method.
234
+ * Returns a union of null and the original shape with null as the default placeholder.
235
+ */
236
+ type WithNullable<S extends ValueShape> = {
237
+ nullable(): WithPlaceholder<UnionValueShape<[NullValueShape, S]>>;
238
+ };
232
239
  interface DocShape<NestedShapes extends Record<string, ContainerShape> = Record<string, ContainerShape>> extends Shape<{
233
240
  [K in keyof NestedShapes]: NestedShapes[K]["_plain"];
234
241
  }, {
@@ -404,9 +411,9 @@ declare const Shape: {
404
411
  text: () => WithPlaceholder<TextContainerShape>;
405
412
  tree: <T extends MapContainerShape | StructContainerShape>(shape: T) => TreeContainerShape;
406
413
  plain: {
407
- string: <T extends string = string>(...options: T[]) => WithPlaceholder<StringValueShape<T>>;
408
- number: () => WithPlaceholder<NumberValueShape>;
409
- boolean: () => WithPlaceholder<BooleanValueShape>;
414
+ string: <T extends string = string>(...options: T[]) => WithPlaceholder<StringValueShape<T>> & WithNullable<StringValueShape<T>>;
415
+ number: () => WithPlaceholder<NumberValueShape> & WithNullable<NumberValueShape>;
416
+ boolean: () => WithPlaceholder<BooleanValueShape> & WithNullable<BooleanValueShape>;
410
417
  null: () => NullValueShape;
411
418
  undefined: () => UndefinedValueShape;
412
419
  uint8Array: () => Uint8ArrayValueShape;
@@ -422,13 +429,13 @@ declare const Shape: {
422
429
  * })
423
430
  * ```
424
431
  */
425
- struct: <T extends Record<string, ValueShape>>(shape: T) => StructValueShape<T>;
432
+ struct: <T extends Record<string, ValueShape>>(shape: T) => StructValueShape<T> & WithNullable<StructValueShape<T>>;
426
433
  /**
427
434
  * @deprecated Use `Shape.plain.struct` instead. `Shape.plain.struct` will be removed in a future version.
428
435
  */
429
- object: <T extends Record<string, ValueShape>>(shape: T) => StructValueShape<T>;
430
- record: <T extends ValueShape>(shape: T) => RecordValueShape<T>;
431
- array: <T extends ValueShape>(shape: T) => ArrayValueShape<T>;
436
+ object: <T extends Record<string, ValueShape>>(shape: T) => StructValueShape<T> & WithNullable<StructValueShape<T>>;
437
+ record: <T extends ValueShape>(shape: T) => RecordValueShape<T> & WithNullable<RecordValueShape<T>>;
438
+ array: <T extends ValueShape>(shape: T) => ArrayValueShape<T> & WithNullable<ArrayValueShape<T>>;
432
439
  union: <T extends ValueShape[]>(shapes: T) => WithPlaceholder<UnionValueShape<T>>;
433
440
  /**
434
441
  * Creates a discriminated union shape for type-safe tagged unions.
@@ -816,4 +823,4 @@ declare class TypedPresence<S extends ContainerShape | ValueShape> {
816
823
  */
817
824
  declare function validatePlaceholder<T extends DocShape>(placeholder: unknown, schema: T): Infer<T>;
818
825
 
819
- export { type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type DiscriminatedUnionValueShape, type DocShape, type Infer, type InferMutableType, type InferPlaceholderType, type ListContainerShape, type MapContainerShape, type MovableListContainerShape, type Mutable, type ObjectValue, type ObjectValueShape, type PresenceInterface, type RecordContainerShape, type RecordValueShape, type ContainerType as RootContainerType, Shape, type StructContainerShape, type StructValueShape, type TextContainerShape, type TreeContainerShape, type TypedDoc, TypedPresence, type UnionValueShape, type ValueShape, type WithPlaceholder, change, createPlaceholderProxy, createTypedDoc, derivePlaceholder, deriveShapePlaceholder, getLoroDoc, mergeValue, overlayPlaceholder, validatePlaceholder };
826
+ export { type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type DiscriminatedUnionValueShape, type DocShape, type Infer, type InferMutableType, type InferPlaceholderType, type ListContainerShape, type MapContainerShape, type MovableListContainerShape, type Mutable, type ObjectValue, type ObjectValueShape, type PresenceInterface, type RecordContainerShape, type RecordValueShape, type ContainerType as RootContainerType, Shape, type StructContainerShape, type StructValueShape, type TextContainerShape, type TreeContainerShape, type TypedDoc, TypedPresence, type UnionValueShape, type ValueShape, type WithNullable, type WithPlaceholder, change, createPlaceholderProxy, createTypedDoc, derivePlaceholder, deriveShapePlaceholder, getLoroDoc, mergeValue, overlayPlaceholder, validatePlaceholder };
package/dist/index.js CHANGED
@@ -270,6 +270,29 @@ function createPlaceholderProxy(target) {
270
270
  }
271
271
 
272
272
  // src/shape.ts
273
+ function makeNullable(shape) {
274
+ const nullShape = {
275
+ _type: "value",
276
+ valueType: "null",
277
+ _plain: null,
278
+ _mutable: null,
279
+ _placeholder: null
280
+ };
281
+ const base = {
282
+ _type: "value",
283
+ valueType: "union",
284
+ shapes: [nullShape, shape],
285
+ _plain: null,
286
+ _mutable: null,
287
+ _placeholder: null
288
+ // Default placeholder is null
289
+ };
290
+ return Object.assign(base, {
291
+ placeholder(value) {
292
+ return { ...base, _placeholder: value };
293
+ }
294
+ });
295
+ }
273
296
  var Shape = {
274
297
  doc: (shape) => ({
275
298
  _type: "doc",
@@ -382,6 +405,9 @@ var Shape = {
382
405
  return Object.assign(base, {
383
406
  placeholder(value) {
384
407
  return { ...base, _placeholder: value };
408
+ },
409
+ nullable() {
410
+ return makeNullable(base);
385
411
  }
386
412
  });
387
413
  },
@@ -396,6 +422,9 @@ var Shape = {
396
422
  return Object.assign(base, {
397
423
  placeholder(value) {
398
424
  return { ...base, _placeholder: value };
425
+ },
426
+ nullable() {
427
+ return makeNullable(base);
399
428
  }
400
429
  });
401
430
  },
@@ -410,6 +439,9 @@ var Shape = {
410
439
  return Object.assign(base, {
411
440
  placeholder(value) {
412
441
  return { ...base, _placeholder: value };
442
+ },
443
+ nullable() {
444
+ return makeNullable(base);
413
445
  }
414
446
  });
415
447
  },
@@ -446,41 +478,69 @@ var Shape = {
446
478
  * })
447
479
  * ```
448
480
  */
449
- struct: (shape) => ({
450
- _type: "value",
451
- valueType: "struct",
452
- shape,
453
- _plain: {},
454
- _mutable: {},
455
- _placeholder: {}
456
- }),
481
+ struct: (shape) => {
482
+ const base = {
483
+ _type: "value",
484
+ valueType: "struct",
485
+ shape,
486
+ _plain: {},
487
+ _mutable: {},
488
+ _placeholder: {}
489
+ };
490
+ return Object.assign(base, {
491
+ nullable() {
492
+ return makeNullable(base);
493
+ }
494
+ });
495
+ },
457
496
  /**
458
497
  * @deprecated Use `Shape.plain.struct` instead. `Shape.plain.struct` will be removed in a future version.
459
498
  */
460
- object: (shape) => ({
461
- _type: "value",
462
- valueType: "struct",
463
- shape,
464
- _plain: {},
465
- _mutable: {},
466
- _placeholder: {}
467
- }),
468
- record: (shape) => ({
469
- _type: "value",
470
- valueType: "record",
471
- shape,
472
- _plain: {},
473
- _mutable: {},
474
- _placeholder: {}
475
- }),
476
- array: (shape) => ({
477
- _type: "value",
478
- valueType: "array",
479
- shape,
480
- _plain: [],
481
- _mutable: [],
482
- _placeholder: []
483
- }),
499
+ object: (shape) => {
500
+ const base = {
501
+ _type: "value",
502
+ valueType: "struct",
503
+ shape,
504
+ _plain: {},
505
+ _mutable: {},
506
+ _placeholder: {}
507
+ };
508
+ return Object.assign(base, {
509
+ nullable() {
510
+ return makeNullable(base);
511
+ }
512
+ });
513
+ },
514
+ record: (shape) => {
515
+ const base = {
516
+ _type: "value",
517
+ valueType: "record",
518
+ shape,
519
+ _plain: {},
520
+ _mutable: {},
521
+ _placeholder: {}
522
+ };
523
+ return Object.assign(base, {
524
+ nullable() {
525
+ return makeNullable(base);
526
+ }
527
+ });
528
+ },
529
+ array: (shape) => {
530
+ const base = {
531
+ _type: "value",
532
+ valueType: "array",
533
+ shape,
534
+ _plain: [],
535
+ _mutable: [],
536
+ _placeholder: []
537
+ };
538
+ return Object.assign(base, {
539
+ nullable() {
540
+ return makeNullable(base);
541
+ }
542
+ });
543
+ },
484
544
  // Special value type that helps make things like `string | null` representable
485
545
  // TODO(duane): should this be a more general type for containers too?
486
546
  union: (shapes) => {