jazz-tools 0.16.2 → 0.16.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.
Files changed (73) hide show
  1. package/.turbo/turbo-build.log +33 -33
  2. package/CHANGELOG.md +11 -0
  3. package/dist/{chunk-CL3ROOZM.js → chunk-RAMDW4VL.js} +83 -29
  4. package/dist/chunk-RAMDW4VL.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/react-core/hooks.d.ts +150 -2
  7. package/dist/react-core/hooks.d.ts.map +1 -1
  8. package/dist/react-core/index.js.map +1 -1
  9. package/dist/testing.js +1 -1
  10. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  11. package/dist/tools/coValues/coList.d.ts.map +1 -1
  12. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  13. package/dist/tools/coValues/schemaUnion.d.ts +7 -2
  14. package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
  15. package/dist/tools/exports.d.ts +1 -1
  16. package/dist/tools/exports.d.ts.map +1 -1
  17. package/dist/tools/implementation/schema.d.ts +12 -2
  18. package/dist/tools/implementation/schema.d.ts.map +1 -1
  19. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +2 -4
  20. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
  21. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +2 -4
  22. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
  23. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +3 -6
  24. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  25. package/dist/tools/implementation/zodSchema/typeConverters/CoFieldInit.d.ts +20 -6
  26. package/dist/tools/implementation/zodSchema/typeConverters/CoFieldInit.d.ts.map +1 -1
  27. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts +4 -4
  28. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts.map +1 -1
  29. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts +3 -3
  30. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts.map +1 -1
  31. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.d.ts +3 -25
  32. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.d.ts.map +1 -1
  33. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.d.ts +3 -25
  34. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.d.ts.map +1 -1
  35. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts +24 -0
  36. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts.map +1 -0
  37. package/dist/tools/implementation/zodSchema/unionUtils.d.ts +2 -3
  38. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  39. package/dist/tools/implementation/zodSchema/zodSchema.d.ts +3 -4
  40. package/dist/tools/implementation/zodSchema/zodSchema.d.ts.map +1 -1
  41. package/dist/tools/internal.d.ts +1 -0
  42. package/dist/tools/internal.d.ts.map +1 -1
  43. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  44. package/package.json +4 -4
  45. package/src/react-core/hooks.ts +143 -0
  46. package/src/tools/coValues/coFeed.ts +12 -4
  47. package/src/tools/coValues/coList.ts +41 -19
  48. package/src/tools/coValues/coMap.ts +12 -2
  49. package/src/tools/coValues/inbox.ts +1 -1
  50. package/src/tools/coValues/schemaUnion.ts +28 -3
  51. package/src/tools/exports.ts +0 -1
  52. package/src/tools/implementation/schema.ts +28 -2
  53. package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +2 -7
  54. package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +2 -7
  55. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +2 -10
  56. package/src/tools/implementation/zodSchema/typeConverters/CoFieldInit.ts +74 -9
  57. package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.ts +9 -4
  58. package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.ts +3 -3
  59. package/src/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.ts +3 -114
  60. package/src/tools/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.ts +5 -129
  61. package/src/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.ts +79 -0
  62. package/src/tools/implementation/zodSchema/unionUtils.ts +6 -9
  63. package/src/tools/implementation/zodSchema/zodSchema.ts +3 -13
  64. package/src/tools/internal.ts +1 -0
  65. package/src/tools/subscribe/SubscriptionScope.ts +4 -2
  66. package/src/tools/subscribe/utils.ts +2 -2
  67. package/src/tools/tests/coFeed.test.ts +40 -0
  68. package/src/tools/tests/coList.test.ts +42 -0
  69. package/src/tools/tests/coMap.test-d.ts +41 -1
  70. package/src/tools/tests/coMap.test.ts +82 -5
  71. package/src/tools/tests/groupsAndAccounts.test.ts +54 -0
  72. package/src/tools/tests/zod.test.ts +16 -0
  73. package/dist/chunk-CL3ROOZM.js.map +0 -1
@@ -1,137 +1,13 @@
1
- import { JsonValue } from "cojson";
2
1
  import {
3
- Account,
4
2
  AnyZodOrCoValueSchema,
5
- CoDiscriminatedUnionSchema,
6
- CoFeed,
7
- CoList,
8
- CoMap,
9
- CoPlainText,
10
- CoRichText,
11
3
  CoValueClass,
12
- CoreAccountSchema,
13
- CoreCoRecordSchema,
14
- FileStream,
15
- InstanceOrPrimitiveOfSchema,
16
- Profile,
4
+ InstanceOfSchemaCoValuesNullable,
17
5
  } from "../../../internal.js";
18
- import { CoreCoFeedSchema } from "../schemaTypes/CoFeedSchema.js";
19
- import { CoreCoListSchema } from "../schemaTypes/CoListSchema.js";
20
- import { CoreCoMapSchema } from "../schemaTypes/CoMapSchema.js";
21
- import { CoreCoOptionalSchema } from "../schemaTypes/CoOptionalSchema.js";
22
- import { CoreCoValueSchema } from "../schemaTypes/CoValueSchema.js";
23
- import { CoreFileStreamSchema } from "../schemaTypes/FileStreamSchema.js";
24
- import { CorePlainTextSchema } from "../schemaTypes/PlainTextSchema.js";
25
- import { CoreRichTextSchema } from "../schemaTypes/RichTextSchema.js";
26
6
  import { z } from "../zodReExport.js";
7
+ import { TypeOfZodSchema } from "./TypeOfZodSchema.js";
27
8
 
28
9
  export type InstanceOrPrimitiveOfSchemaCoValuesNullable<
29
10
  S extends CoValueClass | AnyZodOrCoValueSchema,
30
- > = S extends CoreCoValueSchema
31
- ? S extends CoreAccountSchema<infer Shape>
32
- ?
33
- | ({
34
- -readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
35
- Shape[key]
36
- >;
37
- } & { profile: Profile | null } & Account)
38
- | null
39
- : S extends CoreCoRecordSchema<infer K, infer V>
40
- ?
41
- | ({
42
- -readonly [key in z.output<K> &
43
- string]: InstanceOrPrimitiveOfSchemaCoValuesNullable<V>;
44
- } & CoMap)
45
- | null
46
- : S extends CoreCoMapSchema<infer Shape, infer CatchAll>
47
- ?
48
- | ({
49
- -readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchemaCoValuesNullable<
50
- Shape[key]
51
- >;
52
- } & (CatchAll extends AnyZodOrCoValueSchema
53
- ? {
54
- [
55
- key: string
56
- ]: InstanceOrPrimitiveOfSchemaCoValuesNullable<CatchAll>;
57
- }
58
- : {}) &
59
- CoMap)
60
- | null
61
- : S extends CoreCoListSchema<infer T>
62
- ? CoList<InstanceOrPrimitiveOfSchemaCoValuesNullable<T>> | null
63
- : S extends CoreCoFeedSchema<infer T>
64
- ? CoFeed<InstanceOrPrimitiveOfSchemaCoValuesNullable<T>> | null
65
- : S extends CorePlainTextSchema
66
- ? CoPlainText | null
67
- : S extends CoreRichTextSchema
68
- ? CoRichText | null
69
- : S extends CoreFileStreamSchema
70
- ? FileStream | null
71
- : S extends CoreCoOptionalSchema<infer T>
72
- ? InstanceOrPrimitiveOfSchemaCoValuesNullable<T> | undefined
73
- : S extends CoDiscriminatedUnionSchema<infer Members>
74
- ? InstanceOrPrimitiveOfSchemaCoValuesNullable<
75
- Members[number]
76
- >
77
- : never
78
- : S extends z.core.$ZodType
79
- ? S extends z.core.$ZodOptional<infer Inner extends z.core.$ZodType>
80
- ? InstanceOrPrimitiveOfSchemaCoValuesNullable<Inner> | undefined
81
- : S extends z.core.$ZodNullable<infer Inner extends z.core.$ZodType>
82
- ? InstanceOrPrimitiveOfSchemaCoValuesNullable<Inner> | null
83
- : S extends z.ZodJSONSchema
84
- ? JsonValue
85
- : S extends z.core.$ZodUnion<
86
- infer Members extends readonly z.core.$ZodType[]
87
- >
88
- ? InstanceOrPrimitiveOfSchemaCoValuesNullable<Members[number]>
89
- : // primitives below here - we manually traverse to ensure we only allow what we can handle
90
- S extends z.core.$ZodObject<infer Shape>
91
- ? {
92
- -readonly [key in keyof Shape]: InstanceOrPrimitiveOfSchema<
93
- Shape[key]
94
- >;
95
- }
96
- : S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
97
- ? InstanceOrPrimitiveOfSchema<Item>[]
98
- : S extends z.core.$ZodTuple<
99
- infer Items extends z.core.$ZodType[]
100
- >
101
- ? {
102
- [key in keyof Items]: InstanceOrPrimitiveOfSchema<
103
- Items[key]
104
- >;
105
- }
106
- : S extends z.core.$ZodString
107
- ? string
108
- : S extends z.core.$ZodNumber
109
- ? number
110
- : S extends z.core.$ZodBoolean
111
- ? boolean
112
- : S extends z.core.$ZodLiteral<infer Literal>
113
- ? Literal
114
- : S extends z.core.$ZodDate
115
- ? Date
116
- : S extends z.core.$ZodEnum<infer Enum>
117
- ? Enum[keyof Enum]
118
- : S extends z.core.$ZodTemplateLiteral<
119
- infer pattern
120
- >
121
- ? pattern
122
- : S extends z.core.$ZodReadonly<
123
- infer Inner extends z.core.$ZodType
124
- >
125
- ? InstanceOrPrimitiveOfSchema<Inner>
126
- : S extends z.core.$ZodDefault<
127
- infer Default extends z.core.$ZodType
128
- >
129
- ? InstanceOrPrimitiveOfSchema<Default>
130
- : S extends z.core.$ZodCatch<
131
- infer Catch extends z.core.$ZodType
132
- >
133
- ? InstanceOrPrimitiveOfSchema<Catch>
134
- : never
135
- : S extends CoValueClass
136
- ? InstanceType<S> | null
137
- : never;
11
+ > = S extends z.core.$ZodType
12
+ ? TypeOfZodSchema<S>
13
+ : InstanceOfSchemaCoValuesNullable<S>;
@@ -0,0 +1,79 @@
1
+ import { JsonValue } from "cojson";
2
+ import { PartialOnUndefined } from "../../../internal.js";
3
+ import { z } from "../zodReExport.js";
4
+
5
+ // Copied from https://github.com/colinhacks/zod/blob/7e7e3461aceecf3633e158df50d6bc852e7cdf45/packages/zod/src/v4/core/schemas.ts#L1591,
6
+ // since this type is not exported by Zod
7
+ type OptionalInSchema = {
8
+ _zod: {
9
+ optin: "optional";
10
+ };
11
+ };
12
+
13
+ /**
14
+ * Get type from Zod schema definition.
15
+ *
16
+ * Similar to `z.infer`, but we manually traverse Zod types to ensure we only allow what we can handle
17
+ */
18
+ export type TypeOfZodSchema<S extends z.core.$ZodType> =
19
+ S extends z.core.$ZodOptional<infer Inner extends z.core.$ZodType>
20
+ ? TypeOfZodSchema<Inner> | undefined
21
+ : S extends z.core.$ZodNullable<infer Inner extends z.core.$ZodType>
22
+ ? TypeOfZodSchema<Inner> | null
23
+ : S extends z.ZodJSONSchema
24
+ ? JsonValue
25
+ : S extends z.core.$ZodUnion<
26
+ infer Members extends readonly z.core.$ZodType[]
27
+ >
28
+ ? TypeOfZodSchema<Members[number]>
29
+ : S extends z.core.$ZodObject<infer Shape>
30
+ ? /**
31
+ * Cannot use {@link PartialOnUndefined} because evaluating TypeOfZodSchema<Shape[key]>
32
+ * to know if the value can be undefined does not work with recursive types.
33
+ */
34
+ {
35
+ -readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
36
+ ? never
37
+ : key]: TypeOfZodSchema<Shape[key]>;
38
+ } & {
39
+ -readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
40
+ ? key
41
+ : never]?: TypeOfZodSchema<Shape[key]>;
42
+ }
43
+ : S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
44
+ ? TypeOfZodSchema<Item>[]
45
+ : S extends z.core.$ZodTuple<
46
+ infer Items extends readonly z.core.$ZodType[]
47
+ >
48
+ ? {
49
+ [key in keyof Items]: TypeOfZodSchema<Items[key]>;
50
+ }
51
+ : S extends z.core.$ZodString
52
+ ? string
53
+ : S extends z.core.$ZodNumber
54
+ ? number
55
+ : S extends z.core.$ZodBoolean
56
+ ? boolean
57
+ : S extends z.core.$ZodLiteral<infer Literal>
58
+ ? Literal
59
+ : S extends z.core.$ZodDate
60
+ ? Date
61
+ : S extends z.core.$ZodEnum<infer Enum>
62
+ ? Enum[keyof Enum]
63
+ : S extends z.core.$ZodTemplateLiteral<
64
+ infer pattern
65
+ >
66
+ ? pattern
67
+ : S extends z.core.$ZodReadonly<
68
+ infer Inner extends z.core.$ZodType
69
+ >
70
+ ? TypeOfZodSchema<Inner>
71
+ : S extends z.core.$ZodDefault<
72
+ infer Default extends z.core.$ZodType
73
+ >
74
+ ? TypeOfZodSchema<Default>
75
+ : S extends z.core.$ZodCatch<
76
+ infer Catch extends z.core.$ZodType
77
+ >
78
+ ? TypeOfZodSchema<Catch>
79
+ : never;
@@ -7,6 +7,7 @@ import {
7
7
  CoreCoMapSchema,
8
8
  DiscriminableCoValueSchemas,
9
9
  DiscriminableCoreCoValueSchema,
10
+ SchemaUnionDiscriminator,
10
11
  } from "../../internal.js";
11
12
  import {
12
13
  hydrateCoreCoValueSchema,
@@ -56,21 +57,17 @@ export function schemaUnionDiscriminatorFor(
56
57
  }
57
58
  }
58
59
 
59
- const determineSchema = (_raw: RawCoMap | RawAccount | RawCoList) => {
60
- if (_raw instanceof RawCoList) {
61
- throw new Error(
62
- "co.discriminatedUnion() of collaborative types is not supported for CoLists",
63
- );
64
- }
65
-
60
+ const determineSchema: SchemaUnionDiscriminator<CoMap> = (
61
+ discriminable,
62
+ ) => {
66
63
  for (const option of availableOptions) {
67
64
  let match = true;
68
65
 
69
66
  for (const key of Object.keys(discriminatorMap)) {
70
67
  const discriminatorDef = (option as CoreCoMapSchema).getDefinition()
71
- .shape[key as string];
68
+ .shape[key];
72
69
 
73
- const discriminatorValue = (_raw as RawCoMap).get(key as string);
70
+ const discriminatorValue = discriminable.get(key);
74
71
 
75
72
  if (discriminatorValue && typeof discriminatorValue === "object") {
76
73
  throw new Error("Discriminator must be a primitive value");
@@ -23,11 +23,7 @@ import {
23
23
  } from "./schemaTypes/CoDiscriminatedUnionSchema.js";
24
24
  import { CoFeedSchema, CoreCoFeedSchema } from "./schemaTypes/CoFeedSchema.js";
25
25
  import { CoListSchema, CoreCoListSchema } from "./schemaTypes/CoListSchema.js";
26
- import {
27
- CoMapSchema,
28
- CoMapSchemaInit,
29
- CoreCoMapSchema,
30
- } from "./schemaTypes/CoMapSchema.js";
26
+ import { CoMapSchema, CoreCoMapSchema } from "./schemaTypes/CoMapSchema.js";
31
27
  import {
32
28
  CoOptionalSchema,
33
29
  CoreCoOptionalSchema,
@@ -84,8 +80,8 @@ export type CoValueSchemaFromCoreSchema<S extends CoreCoValueSchema> =
84
80
  export type CoValueClassFromAnySchema<S extends CoValueClassOrSchema> =
85
81
  S extends CoValueClass<any>
86
82
  ? S
87
- : CoValueClass<InstanceOfSchema<S>> &
88
- CoValueFromRaw<InstanceOfSchema<S>> &
83
+ : CoValueClass<NonNullable<InstanceOfSchema<S>>> &
84
+ CoValueFromRaw<NonNullable<InstanceOfSchema<S>>> &
89
85
  (S extends CoreAccountSchema ? AccountClassEssentials : {});
90
86
 
91
87
  type AccountClassEssentials = {
@@ -122,9 +118,3 @@ export type ResolveQueryStrict<
122
118
  T extends CoValueClassOrSchema,
123
119
  R extends ResolveQuery<T>,
124
120
  > = RefsToResolveStrict<NonNullable<InstanceOfSchemaCoValuesNullable<T>>, R>;
125
-
126
- export type InitFor<T extends CoValueClassOrSchema> = T extends CoreCoMapSchema<
127
- infer Shape
128
- >
129
- ? Simplify<CoMapSchemaInit<Shape>>
130
- : never;
@@ -45,6 +45,7 @@ export * from "./implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSc
45
45
  export * from "./implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.js";
46
46
  export * from "./implementation/zodSchema/typeConverters/InstanceOfSchema.js";
47
47
  export * from "./implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.js";
48
+ export * from "./implementation/zodSchema/typeConverters/CoFieldInit.js";
48
49
  export * from "./implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.js";
49
50
  export * from "./implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.js";
50
51
  export * from "./coValues/extensions/imageDef.js";
@@ -7,7 +7,7 @@ import {
7
7
  type ID,
8
8
  type RefEncoded,
9
9
  type RefsToResolve,
10
- instantiateRefEncoded,
10
+ instantiateRefEncodedFromRaw,
11
11
  isRefEncoded,
12
12
  } from "../internal.js";
13
13
  import { applyCoValueMigrations } from "../lib/migration.js";
@@ -75,7 +75,9 @@ export class SubscriptionScope<D extends CoValue> {
75
75
  }
76
76
 
77
77
  this.migrating = true;
78
- applyCoValueMigrations(instantiateRefEncoded(this.schema, value));
78
+ applyCoValueMigrations(
79
+ instantiateRefEncodedFromRaw(this.schema, value),
80
+ );
79
81
  this.migrated = true;
80
82
  this.handleUpdate(lastUpdate);
81
83
  return;
@@ -4,7 +4,7 @@ import {
4
4
  CoValue,
5
5
  RefEncoded,
6
6
  coValueClassFromCoValueClassOrSchema,
7
- instantiateRefEncoded,
7
+ instantiateRefEncodedFromRaw,
8
8
  } from "../internal.js";
9
9
  import { coValuesCache } from "../lib/cache.js";
10
10
  import { SubscriptionScope } from "./SubscriptionScope.js";
@@ -26,7 +26,7 @@ export function createCoValue<D extends CoValue>(
26
26
  raw: RawCoValue,
27
27
  subscriptionScope: SubscriptionScope<D>,
28
28
  ) {
29
- const freshValueInstance = instantiateRefEncoded(ref, raw);
29
+ const freshValueInstance = instantiateRefEncodedFromRaw(ref, raw);
30
30
 
31
31
  Object.defineProperty(freshValueInstance, "_subscriptionScope", {
32
32
  value: subscriptionScope,
@@ -52,6 +52,46 @@ describe("Simple CoFeed operations", async () => {
52
52
  expect(stream.perSession[me.sessionID]?.value).toEqual("milk");
53
53
  });
54
54
 
55
+ describe("Create CoFeed with a reference", () => {
56
+ let me: Account;
57
+
58
+ beforeEach(async () => {
59
+ await setupJazzTestSync();
60
+ me = await createJazzTestAccount({
61
+ isCurrentActiveAccount: true,
62
+ creationProps: { name: "Hermes Puggington" },
63
+ });
64
+ });
65
+
66
+ test("using a CoValue", () => {
67
+ const Text = co.plainText();
68
+ const TextStream = co.feed(Text);
69
+
70
+ const stream = TextStream.create([Text.create("milk")], { owner: me });
71
+
72
+ const coValue = stream.perAccount[me.id]?.value;
73
+ expect(coValue?.toString()).toEqual("milk");
74
+ });
75
+
76
+ describe("using JSON", () => {
77
+ test("automatically creates CoValues for nested objects", () => {
78
+ const Text = co.plainText();
79
+ const TextStream = co.feed(Text);
80
+
81
+ const stream = TextStream.create(["milk"], { owner: me });
82
+
83
+ const coValue = stream.perAccount[me.id]?.value;
84
+ expect(coValue?.toString()).toEqual("milk");
85
+ });
86
+
87
+ test("can create a coPlainText from an empty string", () => {
88
+ const Schema = co.feed(co.plainText());
89
+ const feed = Schema.create([""]);
90
+ expect(feed.perAccount[me.id]?.value?.toString()).toBe("");
91
+ });
92
+ });
93
+ });
94
+
55
95
  test("Construction with nullable values", () => {
56
96
  const NullableTestStream = co.feed(z.string().nullable());
57
97
  const stream = NullableTestStream.create(["milk", null], { owner: me });
@@ -52,6 +52,48 @@ describe("Simple CoList operations", async () => {
52
52
  expect(list[2]).toBe("c");
53
53
  });
54
54
 
55
+ test("create CoList with reference using CoValue", () => {
56
+ const Dog = co.map({
57
+ name: z.string(),
58
+ });
59
+ const Person = co.map({
60
+ pets: co.list(Dog),
61
+ });
62
+
63
+ const person = Person.create({
64
+ pets: [Dog.create({ name: "Rex" }), Dog.create({ name: "Fido" })],
65
+ });
66
+
67
+ expect(person.pets.length).toEqual(2);
68
+ expect(person.pets[0]?.name).toEqual("Rex");
69
+ expect(person.pets[1]?.name).toEqual("Fido");
70
+ });
71
+
72
+ describe("create CoList with reference using JSON", () => {
73
+ test("automatically creates CoValues for nested objects", () => {
74
+ const Dog = co.map({
75
+ name: z.string(),
76
+ });
77
+ const Person = co.map({
78
+ pets: co.list(Dog),
79
+ });
80
+
81
+ const person = Person.create({
82
+ pets: [{ name: "Rex" }, { name: "Fido" }],
83
+ });
84
+
85
+ expect(person.pets.length).toEqual(2);
86
+ expect(person.pets[0]?.name).toEqual("Rex");
87
+ expect(person.pets[1]?.name).toEqual("Fido");
88
+ });
89
+
90
+ test("can create a coPlainText from an empty string", () => {
91
+ const Schema = co.list(co.plainText());
92
+ const list = Schema.create([""]);
93
+ expect(list[0]?.toString()).toBe("");
94
+ });
95
+ });
96
+
55
97
  test("list with nullable content", () => {
56
98
  const List = co.list(z.string().nullable());
57
99
  const list = List.create(["a", "b", "c", null]);
@@ -54,7 +54,7 @@ describe("CoMap", async () => {
54
54
  expectTypeOf(john._owner).toEqualTypeOf<Account | Group>();
55
55
  });
56
56
 
57
- test("CoMap with reference", () => {
57
+ test("create CoMap with reference using CoValue", () => {
58
58
  const Dog = co.map({
59
59
  name: z.string(),
60
60
  breed: z.string(),
@@ -85,6 +85,46 @@ describe("CoMap", async () => {
85
85
  matches(person);
86
86
  });
87
87
 
88
+ test("create CoMap with reference using JSON", () => {
89
+ const Dog = co.map({
90
+ name: z.string(),
91
+ breed: z.string(),
92
+ });
93
+ const Person = co.map({
94
+ dog1: Dog,
95
+ dog2: Dog,
96
+ get friend() {
97
+ return Person.optional();
98
+ },
99
+ });
100
+
101
+ const person = Person.create({
102
+ // @ts-expect-error - breed is missing
103
+ dog1: { name: "Rex", items },
104
+ // @ts-expect-error - Object literal may only specify known properties
105
+ dog2: { name: "Fido", breed: "Labrador", extra: "extra" },
106
+ friend: {
107
+ dog1: {
108
+ name: "Rex",
109
+ breed: "Labrador",
110
+ },
111
+ dog2: { name: "Fido", breed: "Labrador" },
112
+ },
113
+ });
114
+
115
+ type ExpectedType = {
116
+ dog1: Loaded<typeof Dog>;
117
+ dog2: Loaded<typeof Dog>;
118
+ friend: Loaded<typeof Person> | undefined;
119
+ };
120
+
121
+ function matches(value: ExpectedType) {
122
+ return value;
123
+ }
124
+
125
+ matches(person);
126
+ });
127
+
88
128
  test("CoMap with optional reference", () => {
89
129
  const Dog = co.map({
90
130
  name: z.string(),
@@ -117,10 +117,9 @@ describe("CoMap", async () => {
117
117
  expect(emptyMap.color).toEqual(undefined);
118
118
  });
119
119
 
120
- test("CoMap with reference", () => {
120
+ test("create CoMap with reference using CoValue", () => {
121
121
  const Dog = co.map({
122
122
  name: z.string(),
123
- breed: z.string(),
124
123
  });
125
124
 
126
125
  const Person = co.map({
@@ -132,11 +131,89 @@ describe("CoMap", async () => {
132
131
  const person = Person.create({
133
132
  name: "John",
134
133
  age: 20,
135
- dog: Dog.create({ name: "Rex", breed: "Labrador" }),
134
+ dog: Dog.create({ name: "Rex" }),
136
135
  });
137
136
 
138
137
  expect(person.dog?.name).toEqual("Rex");
139
- expect(person.dog?.breed).toEqual("Labrador");
138
+ });
139
+
140
+ describe("create CoMap with references using JSON", () => {
141
+ const Dog = co.map({
142
+ type: z.literal("dog"),
143
+ name: z.string(),
144
+ });
145
+ const Cat = co.map({
146
+ type: z.literal("cat"),
147
+ name: z.string(),
148
+ });
149
+
150
+ const Person = co.map({
151
+ name: co.plainText(),
152
+ bio: co.richText(),
153
+ dog: Dog,
154
+ get friends() {
155
+ return co.list(Person);
156
+ },
157
+ reactions: co.feed(co.plainText()),
158
+ pet: co.discriminatedUnion("type", [Dog, Cat]),
159
+ });
160
+
161
+ let person: ReturnType<typeof Person.create>;
162
+
163
+ beforeEach(() => {
164
+ person = Person.create({
165
+ name: "John",
166
+ bio: "I am a software engineer",
167
+ dog: { type: "dog", name: "Rex" },
168
+ friends: [
169
+ {
170
+ name: "Jane",
171
+ bio: "I am a mechanical engineer",
172
+ dog: { type: "dog", name: "Fido" },
173
+ friends: [],
174
+ reactions: [],
175
+ pet: { type: "dog", name: "Fido" },
176
+ },
177
+ ],
178
+ reactions: ["👎", "👍"],
179
+ pet: { type: "cat", name: "Whiskers" },
180
+ });
181
+ });
182
+
183
+ it("automatically creates CoValues for each CoValue reference", () => {
184
+ expect(person.name.toString()).toEqual("John");
185
+ expect(person.bio.toString()).toEqual("I am a software engineer");
186
+ expect(person.dog?.name).toEqual("Rex");
187
+ expect(person.friends.length).toEqual(1);
188
+ expect(person.friends[0]?.name.toString()).toEqual("Jane");
189
+ expect(person.friends[0]?.bio.toString()).toEqual(
190
+ "I am a mechanical engineer",
191
+ );
192
+ expect(person.friends[0]?.dog.name).toEqual("Fido");
193
+ expect(person.friends[0]?.friends.length).toEqual(0);
194
+ expect(person.reactions.byMe?.value?.toString()).toEqual("👍");
195
+ expect(person.pet.name).toEqual("Whiskers");
196
+ });
197
+
198
+ it("creates a group for each new CoValue that is a child of the referencing CoValue's owner", () => {
199
+ for (const value of Object.values(person)) {
200
+ expect(
201
+ value._owner.getParentGroups().map((group: Group) => group.id),
202
+ ).toContain(person._owner.id);
203
+ }
204
+ const friend = person.friends[0]!;
205
+ for (const value of Object.values(friend)) {
206
+ expect(
207
+ value._owner.getParentGroups().map((group: Group) => group.id),
208
+ ).toContain(friend._owner.id);
209
+ }
210
+ });
211
+
212
+ it("can create a coPlainText from an empty string", () => {
213
+ const Schema = co.map({ text: co.plainText() });
214
+ const map = Schema.create({ text: "" });
215
+ expect(map.text.toString()).toBe("");
216
+ });
140
217
  });
141
218
 
142
219
  test("CoMap with self reference", () => {
@@ -209,7 +286,7 @@ describe("CoMap", async () => {
209
286
  const Person = co.map({
210
287
  name: z.string(),
211
288
  age: z.number(),
212
- get friend(): co.Optional<typeof Person> {
289
+ get friend() {
213
290
  return co.optional(Person);
214
291
  },
215
292
  });
@@ -316,6 +316,60 @@ describe("Group inheritance", () => {
316
316
  expect(group.getRoleOf(bob.id)).toBe("reader");
317
317
  expect(group.getRoleOf(alice.id)).toBe(undefined);
318
318
  });
319
+
320
+ describe("when creating nested CoValues from a JSON object", () => {
321
+ const Task = co.plainText();
322
+ const Column = co.list(Task);
323
+ const Board = co.map({
324
+ title: z.string(),
325
+ columns: co.list(Column),
326
+ });
327
+
328
+ let board: ReturnType<typeof Board.create>;
329
+
330
+ beforeEach(async () => {
331
+ const me = co.account().getMe();
332
+ const writeAccess = Group.create();
333
+ writeAccess.addMember(me, "writer");
334
+
335
+ board = Board.create(
336
+ {
337
+ title: "My board",
338
+ columns: [
339
+ ["Task 1.1", "Task 1.2"],
340
+ ["Task 2.1", "Task 2.2"],
341
+ ],
342
+ },
343
+ writeAccess,
344
+ );
345
+ });
346
+
347
+ test("nested CoValues inherit permissions from the referencing CoValue", async () => {
348
+ const me = co.account().getMe();
349
+ const task = board.columns[0]![0]!;
350
+
351
+ const boardAsWriter = await Board.load(board.id, { loadAs: me });
352
+ expect(boardAsWriter?.title).toEqual("My board");
353
+ const taskAsWriter = await Task.load(task.id, { loadAs: me });
354
+ expect(taskAsWriter?.toString()).toEqual("Task 1.1");
355
+ });
356
+
357
+ test("nested CoValues inherit permissions from the referencing CoValue", async () => {
358
+ const me = co.account().getMe();
359
+ const reader = await co.account().createAs(me, {
360
+ creationProps: { name: "Reader" },
361
+ });
362
+
363
+ const task = board.columns[0]![0]!;
364
+ const taskGroup = task._owner.castAs(Group);
365
+ taskGroup.addMember(reader, "reader");
366
+
367
+ const taskAsReader = await Task.load(task.id, { loadAs: reader });
368
+ expect(taskAsReader?.toString()).toEqual("Task 1.1");
369
+ const boardAsReader = await Board.load(board.id, { loadAs: reader });
370
+ expect(boardAsReader).toBeNull();
371
+ });
372
+ });
319
373
  });
320
374
 
321
375
  describe("Group.getRoleOf", () => {