jazz-tools 0.7.0-alpha.9 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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 +298 -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 -74
  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 -114
  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
  }