jazz-tools 0.8.3 → 0.8.11

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 (39) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/native/coValues/interfaces.js +15 -0
  3. package/dist/native/coValues/interfaces.js.map +1 -1
  4. package/dist/native/exports.js +11 -0
  5. package/dist/native/exports.js.map +1 -0
  6. package/dist/native/implementation/refs.js +4 -4
  7. package/dist/native/implementation/refs.js.map +1 -1
  8. package/dist/native/implementation/schema.js +14 -9
  9. package/dist/native/implementation/schema.js.map +1 -1
  10. package/dist/native/implementation/subscriptionScope.js +8 -9
  11. package/dist/native/implementation/subscriptionScope.js.map +1 -1
  12. package/dist/native/index.native.js +1 -10
  13. package/dist/native/index.native.js.map +1 -1
  14. package/dist/web/coValues/interfaces.js +15 -0
  15. package/dist/web/coValues/interfaces.js.map +1 -1
  16. package/dist/web/exports.js +11 -0
  17. package/dist/web/exports.js.map +1 -0
  18. package/dist/web/implementation/refs.js +4 -4
  19. package/dist/web/implementation/refs.js.map +1 -1
  20. package/dist/web/implementation/schema.js +14 -9
  21. package/dist/web/implementation/schema.js.map +1 -1
  22. package/dist/web/implementation/subscriptionScope.js +8 -9
  23. package/dist/web/implementation/subscriptionScope.js.map +1 -1
  24. package/dist/web/index.web.js +1 -10
  25. package/dist/web/index.web.js.map +1 -1
  26. package/package.json +6 -6
  27. package/src/coValues/interfaces.ts +34 -0
  28. package/src/exports.ts +35 -0
  29. package/src/implementation/refs.ts +3 -8
  30. package/src/implementation/schema.ts +21 -13
  31. package/src/implementation/subscriptionScope.ts +10 -11
  32. package/src/index.native.ts +2 -35
  33. package/src/index.web.ts +2 -35
  34. package/src/tests/coMap.test.ts +1 -1
  35. package/src/tests/schema.test.ts +205 -0
  36. package/src/tests/subscribe.test.ts +348 -0
  37. package/.turbo/turbo-build.log +0 -8
  38. package/.turbo/turbo-lint.log +0 -4
  39. package/.turbo/turbo-test.log +0 -144
package/src/exports.ts ADDED
@@ -0,0 +1,35 @@
1
+ export type {
2
+ InviteSecret,
3
+ Peer,
4
+ SessionID,
5
+ AgentID,
6
+ SyncMessage,
7
+ CryptoProvider,
8
+ CoValueUniqueness,
9
+ } from "cojson";
10
+
11
+ export type { ID, CoValue } from "./internal.js";
12
+
13
+ export { Encoders, co } from "./internal.js";
14
+
15
+ export { CoMap, type CoMapInit } from "./internal.js";
16
+ export { CoList } from "./internal.js";
17
+ export { CoStream, BinaryCoStream } from "./internal.js";
18
+ export { Group, Profile } from "./internal.js";
19
+ export { Account, isControlledAccount, type AccountClass } from "./internal.js";
20
+ export { ImageDefinition } from "./internal.js";
21
+ export { CoValueBase, type CoValueClass } from "./internal.js";
22
+ export type { DepthsIn, DeeplyLoaded } from "./internal.js";
23
+
24
+ export { loadCoValue, subscribeToCoValue, createCoValueObservable } from "./internal.js";
25
+
26
+ export {
27
+ type AuthMethod,
28
+ type AuthResult,
29
+ createJazzContext,
30
+ fixedCredentialsAuth,
31
+ ephemeralCredentialsAuth,
32
+ AnonymousJazzAgent,
33
+ createAnonymousJazzContext,
34
+ randomSessionProvider,
35
+ } from "./internal.js";
@@ -48,16 +48,13 @@ export class Ref<out V extends CoValue> {
48
48
  }
49
49
  }
50
50
 
51
- private async loadHelper(options?: {
52
- onProgress: (p: number) => void;
53
- }): Promise<V | "unavailable"> {
51
+ private async loadHelper(): Promise<V | "unavailable"> {
54
52
  const node =
55
53
  "node" in this.controlledAccount
56
54
  ? this.controlledAccount.node
57
55
  : this.controlledAccount._raw.core.node;
58
56
  const raw = await node.load(
59
57
  this.id as unknown as CoID<RawCoValue>,
60
- options?.onProgress,
61
58
  );
62
59
  if (raw === "unavailable") {
63
60
  return "unavailable";
@@ -66,10 +63,8 @@ export class Ref<out V extends CoValue> {
66
63
  }
67
64
  }
68
65
 
69
- async load(options?: {
70
- onProgress: (p: number) => void;
71
- }): Promise<V | undefined> {
72
- const result = await this.loadHelper(options);
66
+ async load(): Promise<V | undefined> {
67
+ const result = await this.loadHelper();
73
68
  if (result === "unavailable") {
74
69
  return undefined;
75
70
  } else {
@@ -4,7 +4,19 @@ import {
4
4
  type CoValueClass,
5
5
  isCoValueClass,
6
6
  CoValueFromRaw,
7
+ SchemaInit,
8
+ ItemsSym,
9
+ MembersSym,
7
10
  } from "../internal.js";
11
+ import { CoJsonValue } from "cojson/src/jsonValue.js";
12
+
13
+ /** @category Schema definition */
14
+ export const Encoders = {
15
+ Date: {
16
+ encode: (value: Date) => value.toISOString(),
17
+ decode: (value: JsonValue) => new Date(value as string),
18
+ },
19
+ };
8
20
 
9
21
  export type CoMarker = { readonly __co: unique symbol };
10
22
  /** @category Schema definition */
@@ -18,7 +30,7 @@ export type UnCo<T> = T extends co<infer A> ? A : T;
18
30
 
19
31
  const optional = {
20
32
  ref: optionalRef,
21
- json<T extends JsonValue>(): co<T | undefined> {
33
+ json<T extends CoJsonValue<T>>(): co<T | undefined> {
22
34
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
35
  return { [SchemaInit]: "json" satisfies Schema } as any;
24
36
  },
@@ -38,6 +50,9 @@ const optional = {
38
50
  null: {
39
51
  [SchemaInit]: "json" satisfies Schema,
40
52
  } as unknown as co<null | undefined>,
53
+ Date: {
54
+ [SchemaInit]: { encoded: Encoders.Date } satisfies Schema,
55
+ } as unknown as co<Date | undefined>,
41
56
  literal<T extends (string | number | boolean)[]>(
42
57
  ..._lit: T
43
58
  ): co<T[number] | undefined> {
@@ -60,13 +75,16 @@ export const co = {
60
75
  null: {
61
76
  [SchemaInit]: "json" satisfies Schema,
62
77
  } as unknown as co<null>,
78
+ Date: {
79
+ [SchemaInit]: { encoded: Encoders.Date } satisfies Schema,
80
+ } as unknown as co<Date>,
63
81
  literal<T extends (string | number | boolean)[]>(
64
82
  ..._lit: T
65
83
  ): co<T[number]> {
66
84
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
85
  return { [SchemaInit]: "json" satisfies Schema } as any;
68
86
  },
69
- json<T extends JsonValue>(): co<T> {
87
+ json<T extends CoJsonValue<T>>(): co<T> {
70
88
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
89
  return { [SchemaInit]: "json" satisfies Schema } as any;
72
90
  },
@@ -155,20 +173,10 @@ export type SchemaFor<Field> = NonNullable<Field> extends CoValue
155
173
  export type Encoder<V> = {
156
174
  encode: (value: V) => JsonValue;
157
175
  decode: (value: JsonValue) => V;
158
- };
176
+ }
159
177
  export type OptionalEncoder<V> =
160
178
  | Encoder<V>
161
179
  | {
162
180
  encode: (value: V | undefined) => JsonValue;
163
181
  decode: (value: JsonValue) => V | undefined;
164
182
  };
165
-
166
- import { SchemaInit, ItemsSym, MembersSym } from "./symbols.js";
167
-
168
- /** @category Schema definition */
169
- export const Encoders = {
170
- Date: {
171
- encode: (value: Date) => value.toISOString(),
172
- decode: (value: JsonValue) => new Date(value as string),
173
- },
174
- };
@@ -26,10 +26,10 @@ export class SubscriptionScope<Root extends CoValue> {
26
26
  >();
27
27
  rootEntry: {
28
28
  state: "loaded";
29
- value: Root;
29
+ value: RawCoValue;
30
30
  rawUnsub: () => void;
31
31
  };
32
- onUpdate: (newRoot: Root) => void;
32
+ scheduleUpdate: () => void;
33
33
  scheduledUpdate: boolean = false;
34
34
  cachedValues: { [id: ID<CoValue>]: CoValue } = {};
35
35
  parents: { [id: ID<CoValue>]: Set<ID<CoValue>> } = {};
@@ -41,7 +41,7 @@ export class SubscriptionScope<Root extends CoValue> {
41
41
  ) {
42
42
  this.rootEntry = {
43
43
  state: "loaded" as const,
44
- value: root,
44
+ value: root._raw,
45
45
  rawUnsub: () => {}, // placeholder
46
46
  };
47
47
  this.entries.set(root.id, this.rootEntry);
@@ -49,22 +49,21 @@ export class SubscriptionScope<Root extends CoValue> {
49
49
  subscriptionsScopes.set(root, this);
50
50
 
51
51
  this.subscriber = root._loadedAs;
52
- this.onUpdate = onUpdate;
52
+ this.scheduleUpdate = () => {
53
+ const value = rootSchema.fromRaw(this.rootEntry.value) as Root;
54
+ subscriptionsScopes.set(value, this);
55
+ onUpdate(value);
56
+ };
57
+
53
58
  this.rootEntry.rawUnsub = root._raw.core.subscribe(
54
59
  (rawUpdate: RawCoValue | undefined) => {
55
60
  if (!rawUpdate) return;
56
- this.rootEntry.value = rootSchema.fromRaw(rawUpdate) as Root;
57
- // console.log("root update", this.rootEntry.value.toJSON());
58
- subscriptionsScopes.set(this.rootEntry.value, this);
61
+ this.rootEntry.value = rawUpdate;
59
62
  this.scheduleUpdate();
60
63
  },
61
64
  );
62
65
  }
63
66
 
64
- scheduleUpdate() {
65
- this.onUpdate(this.rootEntry.value);
66
- }
67
-
68
67
  onRefAccessedOrSet(
69
68
  fromId: ID<CoValue>,
70
69
  accessedOrSetId: ID<CoValue> | undefined,
@@ -1,40 +1,7 @@
1
+ export * from "./exports.js";
2
+
1
3
  export {
2
4
  cojsonInternals,
3
5
  MAX_RECOMMENDED_TX_SIZE,
4
6
  PureJSCrypto,
5
7
  } from "cojson/native";
6
- export type {
7
- InviteSecret,
8
- Peer,
9
- SessionID,
10
- AgentID,
11
- SyncMessage,
12
- CryptoProvider,
13
- CoValueUniqueness,
14
- } from "cojson";
15
-
16
- export type { ID, CoValue } from "./internal.js";
17
-
18
- export { Encoders, co } from "./internal.js";
19
-
20
- export { CoMap, type CoMapInit } from "./internal.js";
21
- export { CoList } from "./internal.js";
22
- export { CoStream, BinaryCoStream } from "./internal.js";
23
- export { Group, Profile } from "./internal.js";
24
- export { Account, isControlledAccount, type AccountClass } from "./internal.js";
25
- export { ImageDefinition } from "./internal.js";
26
- export { CoValueBase, type CoValueClass } from "./internal.js";
27
- export type { DepthsIn, DeeplyLoaded } from "./internal.js";
28
-
29
- export { loadCoValue, subscribeToCoValue } from "./internal.js";
30
-
31
- export {
32
- type AuthMethod,
33
- type AuthResult,
34
- createJazzContext,
35
- fixedCredentialsAuth,
36
- ephemeralCredentialsAuth,
37
- AnonymousJazzAgent,
38
- createAnonymousJazzContext,
39
- randomSessionProvider,
40
- } from "./internal.js";
package/src/index.web.ts CHANGED
@@ -1,36 +1,3 @@
1
- export { cojsonInternals, MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from "cojson";
2
- export type {
3
- InviteSecret,
4
- Peer,
5
- SessionID,
6
- AgentID,
7
- SyncMessage,
8
- CryptoProvider,
9
- CoValueUniqueness,
10
- } from "cojson";
11
-
12
- export type { ID, CoValue } from "./internal.js";
13
-
14
- export { Encoders, co } from "./internal.js";
1
+ export * from "./exports.js";
15
2
 
16
- export { CoMap, type CoMapInit } from "./internal.js";
17
- export { CoList } from "./internal.js";
18
- export { CoStream, BinaryCoStream } from "./internal.js";
19
- export { Group, Profile } from "./internal.js";
20
- export { Account, isControlledAccount, type AccountClass } from "./internal.js";
21
- export { ImageDefinition } from "./internal.js";
22
- export { CoValueBase, type CoValueClass } from "./internal.js";
23
- export type { DepthsIn, DeeplyLoaded } from "./internal.js";
24
-
25
- export { loadCoValue, subscribeToCoValue } from "./internal.js";
26
-
27
- export {
28
- type AuthMethod,
29
- type AuthResult,
30
- createJazzContext,
31
- fixedCredentialsAuth,
32
- ephemeralCredentialsAuth,
33
- AnonymousJazzAgent,
34
- createAnonymousJazzContext,
35
- randomSessionProvider,
36
- } from "./internal.js";
3
+ export { cojsonInternals, MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from "cojson";
@@ -18,7 +18,7 @@ const Crypto = await WasmCrypto.create();
18
18
  class TestMap extends CoMap {
19
19
  color = co.string;
20
20
  _height = co.number;
21
- birthday = co.encoded(Encoders.Date);
21
+ birthday = co.Date;
22
22
  name? = co.string;
23
23
  nullable = co.optional.encoded<string | undefined>({
24
24
  encode: (value: string | undefined) => value || null,
@@ -0,0 +1,205 @@
1
+ import { describe, expectTypeOf, it } from "vitest";
2
+ import { CoMap, co } from "../index.web.js";
3
+ import { co as valueWithCoMarker } from "../internal.js";
4
+
5
+ describe("co.json TypeScript validation", () => {
6
+ it("should accept serializable types", async () => {
7
+ type ValidType = { str: string; num: number; bool: boolean };
8
+
9
+ class ValidPrimitiveMap extends CoMap {
10
+ data = co.json<ValidType>();
11
+ }
12
+
13
+ expectTypeOf(ValidPrimitiveMap.create<ValidPrimitiveMap>)
14
+ .parameter(0)
15
+ .toEqualTypeOf<{
16
+ data: valueWithCoMarker<ValidType>;
17
+ }>();
18
+ });
19
+
20
+ it("should accept nested serializable types", async () => {
21
+ type NestedType = {
22
+ outer: {
23
+ inner: {
24
+ value: string;
25
+ };
26
+ };
27
+ }
28
+
29
+ class ValidNestedMap extends CoMap {
30
+ data = co.json<NestedType>();
31
+ }
32
+
33
+ expectTypeOf(ValidNestedMap.create<ValidNestedMap>)
34
+ .parameter(0)
35
+ .toEqualTypeOf<{
36
+ data: valueWithCoMarker<NestedType>;
37
+ }>();
38
+ });
39
+
40
+ it("should accept types with optional attributes", async () => {
41
+ type TypeWithOptional = {
42
+ value: string;
43
+ optional?: string | null;
44
+ }
45
+
46
+ class ValidMap extends CoMap {
47
+ data = co.json<TypeWithOptional>();
48
+ }
49
+
50
+ expectTypeOf(ValidMap.create<ValidMap>)
51
+ .parameter(0)
52
+ .toEqualTypeOf<{
53
+ data: valueWithCoMarker<TypeWithOptional>;
54
+ }>();
55
+ });
56
+
57
+ it("should accept nested serializable interfaces", async () => {
58
+ interface InnerInterface {
59
+ value: string;
60
+ }
61
+
62
+ interface NestedInterface {
63
+ outer: {
64
+ inner: InnerInterface;
65
+ };
66
+ }
67
+
68
+ class ValidNestedMap extends CoMap {
69
+ data = co.json<NestedInterface>();
70
+ }
71
+
72
+ expectTypeOf(ValidNestedMap.create<ValidNestedMap>)
73
+ .parameter(0)
74
+ .toEqualTypeOf<{
75
+ data: valueWithCoMarker<NestedInterface>;
76
+ }>();
77
+ });
78
+
79
+ it("should accept arrays of serializable types", async () => {
80
+ interface ArrayInterface {
81
+ numbers: number[];
82
+ objects: { id: number; name: string }[];
83
+ }
84
+
85
+ class ValidArrayMap extends CoMap {
86
+ data = co.json<ArrayInterface>();
87
+ }
88
+
89
+ expectTypeOf(ValidArrayMap.create<ValidArrayMap>)
90
+ .parameter(0)
91
+ .toEqualTypeOf<{
92
+ data: valueWithCoMarker<ArrayInterface>;
93
+ }>();
94
+ });
95
+
96
+ it("should flag interfaces with functions as invalid", async () => {
97
+ interface InvalidInterface {
98
+ func: () => void;
99
+ }
100
+
101
+ class InvalidFunctionMap extends CoMap {
102
+ // @ts-expect-error Should not be considered valid
103
+ data = co.json<InvalidInterface>();
104
+ }
105
+
106
+ expectTypeOf(InvalidFunctionMap.create<InvalidFunctionMap>)
107
+ .parameter(0)
108
+ .toEqualTypeOf<{
109
+ data: valueWithCoMarker<InvalidInterface>;
110
+ }>();
111
+ });
112
+
113
+ it("should flag types with functions as invalid", async () => {
114
+ type InvalidType = { func: () => void };
115
+
116
+ class InvalidFunctionMap extends CoMap {
117
+ // @ts-expect-error Should not be considered valid
118
+ data = co.json<InvalidType>();
119
+ }
120
+
121
+ expectTypeOf(InvalidFunctionMap.create<InvalidFunctionMap>)
122
+ .parameter(0)
123
+ .toEqualTypeOf<{
124
+ data: valueWithCoMarker<InvalidType>;
125
+ }>();
126
+ });
127
+
128
+ it("should flag types with non-serializable constructors as invalid", async () => {
129
+ type InvalidType = { date: Date; regexp: RegExp; symbol: symbol };
130
+
131
+ class InvalidFunctionMap extends CoMap {
132
+ // @ts-expect-error Should not be considered valid
133
+ data = co.json<InvalidType>();
134
+ }
135
+
136
+ expectTypeOf(InvalidFunctionMap.create<InvalidFunctionMap>)
137
+ .parameter(0)
138
+ .toEqualTypeOf<{
139
+ data: valueWithCoMarker<InvalidType>;
140
+ }>();
141
+ });
142
+
143
+ it("should apply the same validation to optional json", async () => {
144
+ type ValidType = {
145
+ value: string;
146
+ };
147
+
148
+ type InvalidType = {
149
+ value: () => string;
150
+ };
151
+
152
+ class MapWithOptionalJSON extends CoMap {
153
+ data = co.optional.json<ValidType>();
154
+ // @ts-expect-error Should not be considered valid
155
+ data2 = co.optional.json<InvalidType>();
156
+ }
157
+
158
+ expectTypeOf(MapWithOptionalJSON.create<MapWithOptionalJSON>)
159
+ .parameter(0)
160
+ .toEqualTypeOf<{
161
+ data?: valueWithCoMarker<ValidType> | null;
162
+ data2?: valueWithCoMarker<InvalidType> | null;
163
+ }>();
164
+ });
165
+
166
+ it("should not accept functions", async () => {
167
+ class InvalidFunctionMap extends CoMap {
168
+ // @ts-expect-error Should not be considered valid
169
+ data = co.json<() => void>();
170
+ }
171
+
172
+ expectTypeOf(InvalidFunctionMap.create<InvalidFunctionMap>)
173
+ .parameter(0)
174
+ .toEqualTypeOf<{
175
+ data: valueWithCoMarker<() => void>;
176
+ }>();
177
+ });
178
+
179
+ it("should not accept RegExp", async () => {
180
+ class InvalidFunctionMap extends CoMap {
181
+ // @ts-expect-error Should not be considered valid
182
+ data = co.json<RegExp>();
183
+ }
184
+
185
+ expectTypeOf(InvalidFunctionMap.create<InvalidFunctionMap>)
186
+ .parameter(0)
187
+ .toEqualTypeOf<{
188
+ data: valueWithCoMarker<RegExp>;
189
+ }>();
190
+ });
191
+
192
+ it("should accept strings and numbers", async () => {
193
+ class InvalidFunctionMap extends CoMap {
194
+ str = co.json<string>();
195
+ num = co.json<number>();
196
+ }
197
+
198
+ expectTypeOf(InvalidFunctionMap.create<InvalidFunctionMap>)
199
+ .parameter(0)
200
+ .toEqualTypeOf<{
201
+ str: valueWithCoMarker<string>;
202
+ num: valueWithCoMarker<number>;
203
+ }>();
204
+ });
205
+ });