jazz-tools 0.7.0-alpha.8 → 0.7.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.
Files changed (72) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -19
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +304 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +59 -41
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +49 -46
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +143 -44
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +144 -35
  15. package/dist/coValues/coStream.js.map +1 -1
  16. package/dist/coValues/deepLoading.js +60 -0
  17. package/dist/coValues/deepLoading.js.map +1 -0
  18. package/dist/coValues/extensions/imageDef.js +10 -7
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +49 -13
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +70 -31
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +114 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  26. package/dist/implementation/refs.js +58 -18
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +58 -0
  29. package/dist/implementation/schema.js.map +1 -0
  30. package/dist/implementation/subscriptionScope.js +19 -1
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js +5 -0
  33. package/dist/implementation/symbols.js.map +1 -0
  34. package/dist/index.js +3 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/internal.js +5 -2
  37. package/dist/internal.js.map +1 -1
  38. package/dist/tests/coList.test.js +51 -48
  39. package/dist/tests/coList.test.js.map +1 -1
  40. package/dist/tests/coMap.test.js +131 -73
  41. package/dist/tests/coMap.test.js.map +1 -1
  42. package/dist/tests/coStream.test.js +56 -41
  43. package/dist/tests/coStream.test.js.map +1 -1
  44. package/dist/tests/deepLoading.test.js +188 -0
  45. package/dist/tests/deepLoading.test.js.map +1 -0
  46. package/dist/tests/groupsAndAccounts.test.js +83 -0
  47. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  48. package/package.json +17 -9
  49. package/src/coValues/account.ts +113 -125
  50. package/src/coValues/coList.ts +87 -103
  51. package/src/coValues/coMap.ts +200 -147
  52. package/src/coValues/coStream.ts +264 -80
  53. package/src/coValues/deepLoading.ts +229 -0
  54. package/src/coValues/extensions/imageDef.ts +17 -13
  55. package/src/coValues/group.ts +92 -58
  56. package/src/coValues/interfaces.ts +215 -115
  57. package/src/implementation/devtoolsFormatters.ts +110 -0
  58. package/src/implementation/inspect.ts +1 -1
  59. package/src/implementation/refs.ts +80 -28
  60. package/src/implementation/schema.ts +138 -0
  61. package/src/implementation/subscriptionScope.ts +48 -12
  62. package/src/implementation/symbols.ts +11 -0
  63. package/src/index.ts +12 -8
  64. package/src/internal.ts +7 -3
  65. package/src/tests/coList.test.ts +77 -62
  66. package/src/tests/coMap.test.ts +201 -113
  67. package/src/tests/coStream.test.ts +113 -84
  68. package/src/tests/deepLoading.test.ts +301 -0
  69. package/src/tests/groupsAndAccounts.test.ts +91 -0
  70. package/dist/implementation/encoding.js +0 -26
  71. package/dist/implementation/encoding.js.map +0 -1
  72. package/src/implementation/encoding.ts +0 -105
@@ -0,0 +1,229 @@
1
+ import { SessionID } from "cojson";
2
+ import {
3
+ Account,
4
+ CoList,
5
+ CoStream,
6
+ CoStreamEntry,
7
+ ItemsSym,
8
+ Ref,
9
+ RefEncoded,
10
+ UnCo,
11
+ } from "../internal.js";
12
+ import { CoKeys, CoMap } from "./coMap.js";
13
+ import { CoValue, ID } from "./interfaces.js";
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ export function fulfillsDepth(depth: any, value: CoValue): boolean {
17
+ if (
18
+ value._type === "CoMap" ||
19
+ value._type === "Group" ||
20
+ value._type === "Account"
21
+ ) {
22
+ if (Array.isArray(depth) && depth.length === 1) {
23
+ return Object.entries(value).every(([key, item]) => {
24
+ return (
25
+ value as unknown as {
26
+ _refs: { [key: string]: Ref<CoValue> | undefined };
27
+ }
28
+ )._refs[key]
29
+ ? item && fulfillsDepth(depth[0], item)
30
+ : ((value as CoMap)._schema[
31
+ ItemsSym
32
+ ] as RefEncoded<CoValue>)!.optional;
33
+ });
34
+ } else {
35
+ for (const key of Object.keys(depth)) {
36
+ const map = value as unknown as {
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ [key: string]: any;
39
+ _refs: { [key: string]: Ref<CoValue> | undefined };
40
+ };
41
+ if (!map._refs[key] && map._schema[key].optional) {
42
+ continue;
43
+ }
44
+ if (!map[key]) {
45
+ return false;
46
+ }
47
+ if (!fulfillsDepth(depth[key], map[key])) {
48
+ return false;
49
+ }
50
+ }
51
+ return true;
52
+ }
53
+ } else if (value._type === "CoList") {
54
+ if (depth.length === 0) {
55
+ return true;
56
+ } else {
57
+ const itemDepth = depth[0];
58
+ return (value as CoList).every((item, i) =>
59
+ (value as CoList)._refs[i]
60
+ ? item && fulfillsDepth(itemDepth, item)
61
+ : (
62
+ (value as CoList)._schema[
63
+ ItemsSym
64
+ ] as RefEncoded<CoValue>
65
+ ).optional,
66
+ );
67
+ }
68
+ } else if (value._type === "CoStream") {
69
+ if (depth.length === 0) {
70
+ return true;
71
+ } else {
72
+ const itemDepth = depth[0];
73
+ return Object.values((value as CoStream).perSession).every(
74
+ (entry) =>
75
+ entry.ref
76
+ ? entry.value && fulfillsDepth(itemDepth, entry.value)
77
+ : (
78
+ (value as CoStream)._schema[
79
+ ItemsSym
80
+ ] as RefEncoded<CoValue>
81
+ ).optional,
82
+ );
83
+ }
84
+ } else if (value._type === "BinaryCoStream") {
85
+ return true;
86
+ } else {
87
+ console.error(value);
88
+ throw new Error("Unexpected value type: " + value._type);
89
+ }
90
+ }
91
+
92
+ type UnCoNotNull<T> = UnCo<Exclude<T, null>>;
93
+ type Clean<T> = UnCo<NonNullable<T>>;
94
+
95
+ export type DepthsIn<
96
+ V,
97
+ DepthLimit extends number = 5,
98
+ CurrentDepth extends number[] = [],
99
+ > =
100
+ | (DepthLimit extends CurrentDepth["length"]
101
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ any
103
+ : // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
104
+ V extends Array<infer Item>
105
+ ?
106
+ | [
107
+ DepthsIn<
108
+ UnCoNotNull<Item>,
109
+ DepthLimit,
110
+ [0, ...CurrentDepth]
111
+ >,
112
+ ]
113
+ | never[]
114
+ : // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
115
+ V extends { _type: "CoMap" | "Group" | "Account" }
116
+ ?
117
+ | {
118
+ [Key in CoKeys<V> as Clean<V[Key]> extends CoValue
119
+ ? Key
120
+ : never]?: DepthsIn<
121
+ Clean<V[Key]>,
122
+ DepthLimit,
123
+ [0, ...CurrentDepth]
124
+ >;
125
+ }
126
+ | (ItemsSym extends keyof V
127
+ ? [
128
+ DepthsIn<
129
+ Clean<V[ItemsSym]>,
130
+ DepthLimit,
131
+ [0, ...CurrentDepth]
132
+ >,
133
+ ]
134
+ : never)
135
+ | never[]
136
+ : V extends {
137
+ _type: "CoStream";
138
+ byMe: CoStreamEntry<infer Item> | undefined;
139
+ }
140
+ ?
141
+ | [
142
+ DepthsIn<
143
+ UnCoNotNull<Item>,
144
+ DepthLimit,
145
+ [0, ...CurrentDepth]
146
+ >,
147
+ ]
148
+ | never[]
149
+ : never[])
150
+ | never[];
151
+
152
+ export type DeeplyLoaded<
153
+ V,
154
+ Depth,
155
+ DepthLimit extends number = 5,
156
+ CurrentDepth extends number[] = [],
157
+ > = DepthLimit extends CurrentDepth["length"]
158
+ ? V
159
+ : // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
160
+ [V] extends [Array<infer Item>]
161
+ ? Depth extends never[] // []
162
+ ? V
163
+ : UnCoNotNull<Item> extends CoValue
164
+ ? Depth extends Array<infer ItemDepth> // [item-depth]
165
+ ? (UnCoNotNull<Item> &
166
+ DeeplyLoaded<
167
+ UnCoNotNull<Item>,
168
+ ItemDepth,
169
+ DepthLimit,
170
+ [0, ...CurrentDepth]
171
+ >)[] &
172
+ V
173
+ : never
174
+ : V
175
+ : // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
176
+ [V] extends [{ _type: "CoMap" | "Group" | "Account" }]
177
+ ? Depth extends never[]
178
+ ? V
179
+ : Depth extends Array<infer ItemDepth>
180
+ ? ItemsSym extends keyof V
181
+ ? V & {
182
+ [key: string]: DeeplyLoaded<
183
+ Clean<V[ItemsSym]>,
184
+ ItemDepth,
185
+ DepthLimit,
186
+ [0, ...CurrentDepth]
187
+ >;
188
+ }
189
+ : never
190
+ : keyof Depth extends never
191
+ ? V
192
+ : {
193
+ [Key in keyof Depth]-?: Key extends CoKeys<V>
194
+ ? Clean<V[Key]> extends CoValue
195
+ ?
196
+ | DeeplyLoaded<
197
+ Clean<V[Key]>,
198
+ Depth[Key],
199
+ DepthLimit,
200
+ [0, ...CurrentDepth]
201
+ >
202
+ | (undefined extends V[Key]
203
+ ? undefined
204
+ : never)
205
+ : never
206
+ : never;
207
+ } & V
208
+ : [V] extends [
209
+ {
210
+ _type: "CoStream";
211
+ byMe: CoStreamEntry<infer Item> | undefined;
212
+ },
213
+ ]
214
+ ? Depth extends never[]
215
+ ? V
216
+ : V & {
217
+ byMe?: { value: UnCoNotNull<Item> };
218
+ inCurrentSession?: { value: UnCoNotNull<Item> };
219
+ perSession: {
220
+ [key: SessionID]: { value: UnCoNotNull<Item> };
221
+ };
222
+ } & { [key: ID<Account>]: { value: UnCoNotNull<Item> } }
223
+ : [V] extends [
224
+ {
225
+ _type: "BinaryCoStream";
226
+ },
227
+ ]
228
+ ? V
229
+ : never;
@@ -1,28 +1,32 @@
1
1
  import {
2
2
  BinaryCoStream,
3
3
  CoMap,
4
- val,
4
+ co,
5
5
  subscriptionsScopes,
6
6
  } from "../../internal.js";
7
7
 
8
- export class ImageDefinition extends CoMap<ImageDefinition> {
9
- originalSize = val.json<[number, number]>();
10
- placeholderDataURL? = val.string;
8
+ /** @category Media */
9
+ export class ImageDefinition extends CoMap {
10
+ originalSize = co.json<[number, number]>();
11
+ placeholderDataURL? = co.string;
11
12
 
12
- [val.items] = val.ref(() => BinaryCoStream);
13
- [res: `${number}x${number}`]: val<BinaryCoStream | null>;
13
+ [co.items] = co.ref(BinaryCoStream);
14
+ [res: `${number}x${number}`]: co<BinaryCoStream | null>;
14
15
 
15
- get highestResAvailable():
16
- | { res: `${number}x${number}`; stream: BinaryCoStream }
17
- | undefined {
16
+ highestResAvailable(options?: {
17
+ maxWidth?: number;
18
+ }): { res: `${number}x${number}`; stream: BinaryCoStream } | undefined {
18
19
  if (!subscriptionsScopes.get(this)) {
19
20
  console.warn(
20
- "highestResAvailable() only makes sense when used within a subscription."
21
+ "highestResAvailable() only makes sense when used within a subscription.",
21
22
  );
22
23
  }
23
24
 
24
- const resolutions = Object.keys(this).filter((key) =>
25
- key.match(/^\d+x\d+$/)
25
+ const resolutions = Object.keys(this).filter(
26
+ (key) =>
27
+ key.match(/^\d+x\d+$/) &&
28
+ (options?.maxWidth === undefined ||
29
+ Number(key.split("x")[0]) <= options.maxWidth),
26
30
  ) as `${number}x${number}`[];
27
31
 
28
32
  resolutions.sort((a, b) => {
@@ -52,4 +56,4 @@ export class ImageDefinition extends CoMap<ImageDefinition> {
52
56
  }
53
57
  );
54
58
  }
55
- }
59
+ }
@@ -1,71 +1,64 @@
1
- import type { Everyone, RawGroup, Role } from "cojson";
2
- import type { CoValue, ID, JsonEncoded, RefEncoded } from "../internal.js";
1
+ import type { AccountID, Everyone, RawGroup, Role } from "cojson";
2
+ import type { CoValue, ID, RefEncoded, Schema, ClassOf } from "../internal.js";
3
3
  import {
4
4
  Account,
5
5
  CoMap,
6
6
  CoValueBase,
7
7
  Ref,
8
- val,
8
+ co,
9
9
  isControlledAccount,
10
10
  AccountAndGroupProxyHandler,
11
+ MembersSym,
11
12
  } from "../internal.js";
12
13
 
13
- export class Profile extends CoMap<{ name: val<string> }> {
14
- name = val.string;
14
+ /** @category Identity & Permissions */
15
+ export class Profile extends CoMap {
16
+ name = co.string;
15
17
  }
16
18
 
17
- export class Group<
18
- Def extends { profile: Profile | null; root: CoMap | null } = {
19
- profile: Profile | null;
20
- root: CoMap | null;
21
- },
22
- >
23
- extends CoValueBase
24
- implements CoValue<"Group", RawGroup>
25
- {
26
- id!: ID<this>;
27
- _type!: "Group";
19
+ /** @category Identity & Permissions */
20
+ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
21
+ declare id: ID<this>;
22
+ declare _type: "Group";
28
23
  static {
29
24
  this.prototype._type = "Group";
30
25
  }
31
- _raw!: RawGroup;
26
+ declare _raw: RawGroup;
32
27
 
33
- static _encoding: any;
34
- get _encoding(): {
35
- profile: Def["profile"] extends CoValue
36
- ? RefEncoded<Def["profile"]>
37
- : JsonEncoded;
38
- root: Def["root"] extends CoValue
39
- ? RefEncoded<Def["root"]>
40
- : JsonEncoded;
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ static _schema: any;
30
+ get _schema(): {
31
+ profile: Schema;
32
+ root: Schema;
33
+ [MembersSym]: RefEncoded<Account>;
41
34
  } {
42
- return (this.constructor as typeof Group)._encoding;
35
+ return (this.constructor as typeof Group)._schema;
43
36
  }
44
37
  static {
45
- this._encoding = {
46
- profile: { json: true },
47
- root: { json: true },
38
+ this._schema = {
39
+ profile: "json" satisfies Schema,
40
+ root: "json" satisfies Schema,
41
+ [MembersSym]: {
42
+ ref: () => Account,
43
+ optional: false,
44
+ } satisfies Schema,
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
46
  } as any;
49
- Object.defineProperty(this.prototype, "_encoding", {
50
- get: () => this._encoding,
47
+ Object.defineProperty(this.prototype, "_schema", {
48
+ get: () => this._schema,
51
49
  });
52
50
  }
53
51
 
54
- profile!: Def["profile"] extends Profile
55
- ? Def["profile"] | null
56
- : undefined;
52
+ declare profile: Profile | null;
53
+ declare root: CoMap | null;
54
+ declare [MembersSym]: Account | null;
57
55
 
58
- root!: Def["root"] extends CoMap ? Def["root"] | null : undefined;
59
-
60
- get _refs(): {
61
- profile: Def["profile"] extends Profile ? Ref<Def["profile"]> : never;
62
- root: Def["root"] extends CoMap ? Ref<Def["root"]> : never;
63
- } {
56
+ get _refs() {
64
57
  const profileID = this._raw.get("profile") as unknown as
65
- | ID<NonNullable<Def["profile"]>>
58
+ | ID<NonNullable<this["profile"]>>
66
59
  | undefined;
67
60
  const rootID = this._raw.get("root") as unknown as
68
- | ID<NonNullable<Def["root"]>>
61
+ | ID<NonNullable<this["root"]>>
69
62
  | undefined;
70
63
  return {
71
64
  profile:
@@ -73,34 +66,35 @@ export class Group<
73
66
  (new Ref(
74
67
  profileID,
75
68
  this._loadedAs,
76
- this._encoding.profile as RefEncoded<
77
- NonNullable<Def["profile"]>
78
- >
79
- ) as any),
69
+ this._schema.profile as RefEncoded<
70
+ NonNullable<this["profile"]>
71
+ >,
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ ) as any as this["profile"] extends Profile
74
+ ? Ref<this["profile"]>
75
+ : never),
80
76
  root:
81
77
  rootID &&
82
78
  (new Ref(
83
79
  rootID,
84
80
  this._loadedAs,
85
- this._encoding.root as RefEncoded<NonNullable<Def["root"]>>
86
- ) as any),
81
+ this._schema.root as RefEncoded<NonNullable<this["root"]>>,
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ ) as any as this["root"] extends CoMap
84
+ ? Ref<this["root"]>
85
+ : never),
87
86
  };
88
87
  }
89
88
 
90
- constructor(options: { owner: Account | Group });
91
- constructor(init: any, options: { fromRaw: RawGroup });
92
- constructor(init: undefined, options: { owner: Account | Group });
93
- constructor(
94
- init: undefined | { owner: Account | Group },
95
- options?: { fromRaw: RawGroup } | { owner: Account | Group }
96
- ) {
89
+ /** @deprecated Don't use constructor directly, use .create */
90
+ constructor(options: { fromRaw: RawGroup } | { owner: Account | Group }) {
97
91
  super();
98
92
  let raw: RawGroup;
99
93
 
100
94
  if (options && "fromRaw" in options) {
101
95
  raw = options.fromRaw;
102
96
  } else {
103
- const initOwner = options?.owner || init?.owner;
97
+ const initOwner = options.owner;
104
98
  if (!initOwner) throw new Error("No owner provided");
105
99
  if (
106
100
  initOwner instanceof Account &&
@@ -110,7 +104,7 @@ export class Group<
110
104
  raw = rawOwner.createGroup();
111
105
  } else {
112
106
  throw new Error(
113
- "Can only construct group as a controlled account"
107
+ "Can only construct group as a controlled account",
114
108
  );
115
109
  }
116
110
  }
@@ -125,15 +119,55 @@ export class Group<
125
119
 
126
120
  return new Proxy(
127
121
  this,
128
- AccountAndGroupProxyHandler as ProxyHandler<this>
122
+ AccountAndGroupProxyHandler as ProxyHandler<this>,
129
123
  );
130
124
  }
131
125
 
126
+ static create<G extends Group>(
127
+ this: ClassOf<G>,
128
+ options: { owner: Account },
129
+ ) {
130
+ return new this(options);
131
+ }
132
+
132
133
  myRole(): Role | undefined {
133
134
  return this._raw.myRole();
134
135
  }
135
136
 
136
137
  addMember(member: Everyone | Account, role: Role) {
137
138
  this._raw.addMember(member === "everyone" ? member : member._raw, role);
139
+ return this;
140
+ }
141
+
142
+ get members() {
143
+ return this._raw
144
+ .keys()
145
+ .filter((key) => {
146
+ return key === "everyone" || key.startsWith("co_");
147
+ })
148
+ .map((id) => {
149
+ const role = this._raw.get(id as Everyone | AccountID);
150
+ const accountID =
151
+ id === "everyone"
152
+ ? undefined
153
+ : (id as unknown as ID<Account>);
154
+ const ref =
155
+ accountID &&
156
+ new Ref<NonNullable<this[MembersSym]>>(
157
+ accountID,
158
+ this._loadedAs,
159
+ this._schema[MembersSym],
160
+ );
161
+ const accessRef = () => ref?.accessFrom(this, "members." + id);
162
+
163
+ return {
164
+ id: id as unknown as Everyone | ID<this[MembersSym]>,
165
+ role,
166
+ ref,
167
+ get account() {
168
+ return accessRef();
169
+ },
170
+ };
171
+ });
138
172
  }
139
173
  }