jazz-tools 0.7.0-alpha.37 → 0.7.0-alpha.39

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +1 -95
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +19 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +22 -12
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +7 -9
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +11 -3
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +15 -8
  15. package/dist/coValues/coStream.js.map +1 -1
  16. package/dist/coValues/deepLoading.js +57 -0
  17. package/dist/coValues/deepLoading.js.map +1 -0
  18. package/dist/coValues/extensions/imageDef.js +1 -1
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +1 -0
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +34 -28
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +1 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -1
  26. package/dist/implementation/refs.js +4 -2
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +5 -1
  29. package/dist/implementation/schema.js.map +1 -1
  30. package/dist/implementation/subscriptionScope.js +9 -4
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js.map +1 -1
  33. package/dist/index.js +2 -4
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal.js +2 -1
  36. package/dist/internal.js.map +1 -1
  37. package/dist/tests/coList.test.js +20 -17
  38. package/dist/tests/coList.test.js.map +1 -1
  39. package/dist/tests/coMap.test.js +27 -17
  40. package/dist/tests/coMap.test.js.map +1 -1
  41. package/dist/tests/coStream.test.js +39 -24
  42. package/dist/tests/coStream.test.js.map +1 -1
  43. package/dist/tests/deepLoading.test.js +188 -0
  44. package/dist/tests/deepLoading.test.js.map +1 -0
  45. package/dist/tests/groupsAndAccounts.test.js +6 -11
  46. package/dist/tests/groupsAndAccounts.test.js.map +1 -1
  47. package/package.json +12 -6
  48. package/src/coValues/account.ts +60 -58
  49. package/src/coValues/coList.ts +31 -51
  50. package/src/coValues/coMap.ts +27 -21
  51. package/src/coValues/coStream.ts +44 -32
  52. package/src/coValues/deepLoading.ts +215 -0
  53. package/src/coValues/extensions/imageDef.ts +3 -3
  54. package/src/coValues/group.ts +11 -13
  55. package/src/coValues/interfaces.ts +151 -93
  56. package/src/implementation/devtoolsFormatters.ts +1 -0
  57. package/src/implementation/inspect.ts +1 -1
  58. package/src/implementation/refs.ts +29 -15
  59. package/src/implementation/schema.ts +21 -6
  60. package/src/implementation/subscriptionScope.ts +33 -17
  61. package/src/implementation/symbols.ts +0 -1
  62. package/src/index.ts +5 -3
  63. package/src/internal.ts +3 -2
  64. package/src/tests/coList.test.ts +33 -28
  65. package/src/tests/coMap.test.ts +63 -55
  66. package/src/tests/coStream.test.ts +82 -67
  67. package/src/tests/deepLoading.test.ts +301 -0
  68. package/src/tests/groupsAndAccounts.test.ts +9 -18
@@ -0,0 +1,215 @@
1
+ import { SessionID } from "cojson";
2
+ import {
3
+ Account,
4
+ CoList,
5
+ CoStream,
6
+ CoStreamEntry,
7
+ ItemsSym,
8
+ Ref,
9
+ UnCo,
10
+ } from "../internal.js";
11
+ import { CoKeys } from "./coMap.js";
12
+ import { CoValue, ID } from "./interfaces.js";
13
+
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ export function fulfillsDepth(depth: any, value: CoValue): boolean {
16
+ if (
17
+ value._type === "CoMap" ||
18
+ value._type === "Group" ||
19
+ value._type === "Account"
20
+ ) {
21
+ if (Array.isArray(depth) && depth.length === 1) {
22
+ return Object.entries(value).every(([key, item]) => {
23
+ return (
24
+ value as unknown as {
25
+ _refs: { [key: string]: Ref<CoValue> | undefined };
26
+ }
27
+ )._refs[key]
28
+ ? item && fulfillsDepth(depth[0], item)
29
+ : true;
30
+ });
31
+ } else {
32
+ for (const key of Object.keys(depth)) {
33
+ const map = value as unknown as {
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ [key: string]: any;
36
+ _refs: { [key: string]: Ref<CoValue> | undefined };
37
+ };
38
+ if (!map._refs[key]) {
39
+ continue;
40
+ }
41
+ if (!map[key]) {
42
+ return false;
43
+ }
44
+ if (!fulfillsDepth(depth[key], map[key])) {
45
+ return false;
46
+ }
47
+ }
48
+ return true;
49
+ }
50
+ } else if (value._type === "CoList") {
51
+ if (depth.length === 0) {
52
+ return true;
53
+ } else {
54
+ const itemDepth = depth[0];
55
+ return (value as CoList).every((item, i) =>
56
+ (value as CoList)._refs[i]
57
+ ? item && fulfillsDepth(itemDepth, item)
58
+ : true,
59
+ );
60
+ }
61
+ } else if (value._type === "CoStream") {
62
+ if (depth.length === 0) {
63
+ return true;
64
+ } else {
65
+ const itemDepth = depth[0];
66
+ return Object.values((value as CoStream).perSession).every(
67
+ (entry) => entry.value && fulfillsDepth(itemDepth, entry.value),
68
+ );
69
+ }
70
+ } else if (value._type === "BinaryCoStream") {
71
+ return true;
72
+ } else {
73
+ console.error(value);
74
+ throw new Error("Unexpected value type: " + value._type);
75
+ }
76
+ }
77
+
78
+ type UnCoNotNull<T> = UnCo<Exclude<T, null>>;
79
+ type Clean<T> = UnCo<NonNullable<T>>;
80
+
81
+ export type DepthsIn<
82
+ V,
83
+ DepthLimit extends number = 5,
84
+ CurrentDepth extends number[] = [],
85
+ > =
86
+ | (DepthLimit extends CurrentDepth["length"]
87
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ any
89
+ : // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
90
+ V extends Array<infer Item>
91
+ ?
92
+ | [
93
+ DepthsIn<
94
+ UnCoNotNull<Item>,
95
+ DepthLimit,
96
+ [0, ...CurrentDepth]
97
+ >,
98
+ ]
99
+ | never[]
100
+ : // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
101
+ V extends { _type: "CoMap" | "Group" | "Account" }
102
+ ?
103
+ | {
104
+ [Key in CoKeys<V> as Clean<V[Key]> extends CoValue
105
+ ? Key
106
+ : never]?: DepthsIn<
107
+ Clean<V[Key]>,
108
+ DepthLimit,
109
+ [0, ...CurrentDepth]
110
+ >;
111
+ }
112
+ | (ItemsSym extends keyof V
113
+ ? [
114
+ DepthsIn<
115
+ Clean<V[ItemsSym]>,
116
+ DepthLimit,
117
+ [0, ...CurrentDepth]
118
+ >,
119
+ ]
120
+ : never)
121
+ | never[]
122
+ : V extends {
123
+ _type: "CoStream";
124
+ byMe: CoStreamEntry<infer Item> | undefined;
125
+ }
126
+ ?
127
+ | [
128
+ DepthsIn<
129
+ UnCoNotNull<Item>,
130
+ DepthLimit,
131
+ [0, ...CurrentDepth]
132
+ >,
133
+ ]
134
+ | never[]
135
+ : never[])
136
+ | never[];
137
+
138
+ export type DeeplyLoaded<
139
+ V,
140
+ Depth,
141
+ DepthLimit extends number = 5,
142
+ CurrentDepth extends number[] = [],
143
+ > = DepthLimit extends CurrentDepth["length"]
144
+ ? V
145
+ : // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
146
+ [V] extends [Array<infer Item>]
147
+ ? Depth extends never[] // []
148
+ ? V
149
+ : UnCoNotNull<Item> extends CoValue
150
+ ? Depth extends Array<infer ItemDepth> // [item-depth]
151
+ ? (UnCoNotNull<Item> &
152
+ DeeplyLoaded<
153
+ UnCoNotNull<Item>,
154
+ ItemDepth,
155
+ DepthLimit,
156
+ [0, ...CurrentDepth]
157
+ >)[] &
158
+ V
159
+ : never
160
+ : V
161
+ : // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
162
+ [V] extends [{ _type: "CoMap" | "Group" | "Account" }]
163
+ ? Depth extends never[]
164
+ ? V
165
+ : Depth extends Array<infer ItemDepth>
166
+ ? ItemsSym extends keyof V
167
+ ? V & {
168
+ [key: string]: DeeplyLoaded<
169
+ Clean<V[ItemsSym]>,
170
+ ItemDepth,
171
+ DepthLimit,
172
+ [0, ...CurrentDepth]
173
+ >;
174
+ }
175
+ : never
176
+ : keyof Depth extends never
177
+ ? V
178
+ : {
179
+ [Key in keyof Depth]-?: Key extends CoKeys<V>
180
+ ? Clean<V[Key]> extends CoValue
181
+ ?
182
+ | DeeplyLoaded<
183
+ Clean<V[Key]>,
184
+ Depth[Key],
185
+ DepthLimit,
186
+ [0, ...CurrentDepth]
187
+ >
188
+ | (undefined extends V[Key]
189
+ ? undefined
190
+ : never)
191
+ : never
192
+ : never;
193
+ } & V
194
+ : [V] extends [
195
+ {
196
+ _type: "CoStream";
197
+ byMe: CoStreamEntry<infer Item> | undefined;
198
+ },
199
+ ]
200
+ ? Depth extends never[]
201
+ ? V
202
+ : V & {
203
+ byMe?: { value: UnCoNotNull<Item> };
204
+ inCurrentSession?: { value: UnCoNotNull<Item> };
205
+ perSession: {
206
+ [key: SessionID]: { value: UnCoNotNull<Item> };
207
+ };
208
+ } & { [key: ID<Account>]: { value: UnCoNotNull<Item> } }
209
+ : [V] extends [
210
+ {
211
+ _type: "BinaryCoStream";
212
+ },
213
+ ]
214
+ ? V
215
+ : never;
@@ -18,15 +18,15 @@ export class ImageDefinition extends CoMap {
18
18
  }): { res: `${number}x${number}`; stream: BinaryCoStream } | undefined {
19
19
  if (!subscriptionsScopes.get(this)) {
20
20
  console.warn(
21
- "highestResAvailable() only makes sense when used within a subscription."
21
+ "highestResAvailable() only makes sense when used within a subscription.",
22
22
  );
23
23
  }
24
24
 
25
25
  const resolutions = Object.keys(this).filter(
26
26
  (key) =>
27
27
  key.match(/^\d+x\d+$/) &&
28
- ((options?.maxWidth === undefined) ||
29
- Number(key.split("x")[0]) <= options.maxWidth)
28
+ (options?.maxWidth === undefined ||
29
+ Number(key.split("x")[0]) <= options.maxWidth),
30
30
  ) as `${number}x${number}`[];
31
31
 
32
32
  resolutions.sort((a, b) => {
@@ -1,11 +1,5 @@
1
1
  import type { AccountID, Everyone, RawGroup, Role } from "cojson";
2
- import type {
3
- CoValue,
4
- ID,
5
- RefEncoded,
6
- Schema,
7
- ClassOf,
8
- } from "../internal.js";
2
+ import type { CoValue, ID, RefEncoded, Schema, ClassOf } from "../internal.js";
9
3
  import {
10
4
  Account,
11
5
  CoMap,
@@ -31,6 +25,7 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
31
25
  }
32
26
  declare _raw: RawGroup;
33
27
 
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
29
  static _schema: any;
35
30
  get _schema(): {
36
31
  profile: Schema;
@@ -44,6 +39,7 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
44
39
  profile: "json" satisfies Schema,
45
40
  root: "json" satisfies Schema,
46
41
  [MembersSym]: () => Account satisfies Schema,
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
43
  } as any;
48
44
  Object.defineProperty(this.prototype, "_schema", {
49
45
  get: () => this._schema,
@@ -69,7 +65,8 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
69
65
  this._loadedAs,
70
66
  this._schema.profile as RefEncoded<
71
67
  NonNullable<this["profile"]>
72
- >
68
+ >,
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
70
  ) as any as this["profile"] extends Profile
74
71
  ? Ref<this["profile"]>
75
72
  : never),
@@ -78,7 +75,8 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
78
75
  (new Ref(
79
76
  rootID,
80
77
  this._loadedAs,
81
- this._schema.root as RefEncoded<NonNullable<this["root"]>>
78
+ this._schema.root as RefEncoded<NonNullable<this["root"]>>,
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
80
  ) as any as this["root"] extends CoMap
83
81
  ? Ref<this["root"]>
84
82
  : never),
@@ -103,7 +101,7 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
103
101
  raw = rawOwner.createGroup();
104
102
  } else {
105
103
  throw new Error(
106
- "Can only construct group as a controlled account"
104
+ "Can only construct group as a controlled account",
107
105
  );
108
106
  }
109
107
  }
@@ -118,13 +116,13 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
118
116
 
119
117
  return new Proxy(
120
118
  this,
121
- AccountAndGroupProxyHandler as ProxyHandler<this>
119
+ AccountAndGroupProxyHandler as ProxyHandler<this>,
122
120
  );
123
121
  }
124
122
 
125
123
  static create<G extends Group>(
126
124
  this: ClassOf<G>,
127
- options: { owner: Account }
125
+ options: { owner: Account },
128
126
  ) {
129
127
  return new this(options);
130
128
  }
@@ -155,7 +153,7 @@ export class Group extends CoValueBase implements CoValue<"Group", RawGroup> {
155
153
  new Ref<NonNullable<this[MembersSym]>>(
156
154
  accountID,
157
155
  this._loadedAs,
158
- this._schema[MembersSym]
156
+ this._schema[MembersSym],
159
157
  );
160
158
  const accessRef = () => ref?.accessFrom(this, "members." + id);
161
159
 
@@ -1,7 +1,11 @@
1
- import { Effect, Sink, Stream } from "effect";
1
+ import { Effect, Option, Sink, Stream } from "effect";
2
2
  import type { CojsonInternalTypes, RawCoValue } from "cojson";
3
3
  import { RawAccount } from "cojson";
4
- import type { Me, UnavailableError } from "../internal.js";
4
+ import type {
5
+ DeeplyLoaded,
6
+ DepthsIn,
7
+ UnavailableError,
8
+ } from "../internal.js";
5
9
  import {
6
10
  Account,
7
11
  AccountCtx,
@@ -11,52 +15,67 @@ import {
11
15
  inspect,
12
16
  subscriptionsScopes,
13
17
  } from "../internal.js";
18
+ import { fulfillsDepth } from "./deepLoading.js";
14
19
 
15
20
  export type ClassOf<T> = {
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
22
  new (...args: any[]): T;
17
23
  };
18
24
 
19
25
  /** @category Abstract interfaces */
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
27
  export interface CoValueClass<Value extends CoValue = CoValue, Init = any> {
21
- /** @category Construction and loading */
22
28
  new (init: Init, options: { owner: Account | Group }): Value;
23
29
 
24
30
  /** @ignore */
25
31
  fromRaw(raw: Value["_raw"]): Value;
26
32
 
27
- /** @category Construction and loading */
28
- load<V extends Value>(
33
+ /** @category Subscription & Loading */
34
+ load<V extends Value, Depth>(
29
35
  this: ClassOf<V>,
30
36
  id: ID<V>,
31
- options: {
32
- as: Account & Me;
33
- onProgress?: (progress: number) => void;
34
- }
35
- ): Promise<V | undefined>;
37
+ as: Account,
38
+ depth: Depth & DepthsIn<V>,
39
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
40
+ /** @category Subscription & Loading */
41
+ load<V extends Value, Depth>(
42
+ this: ClassOf<V>,
43
+ existing: V,
44
+ depth: Depth & DepthsIn<V>,
45
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
36
46
 
37
- /** @category Construction and loading */
38
- loadEf<V extends Value>(
47
+ /** @category Subscription & Loading */
48
+ loadEf<V extends Value, Depth>(
39
49
  this: ClassOf<V>,
40
- id: ID<V>
41
- ): Effect.Effect<V, UnavailableError, AccountCtx>;
50
+ id: ID<V>,
51
+ depth: Depth & DepthsIn<V>,
52
+ ): Effect.Effect<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx>;
42
53
 
43
- /** @category Subscription */
44
- subscribe<V extends Value, Acc extends Account>(
54
+ /** @category Subscription & Loading */
55
+ subscribe<V extends Value, Depth>(
45
56
  this: ClassOf<V>,
46
57
  id: ID<V>,
47
- options: { as: Acc & Me, require?: (value: V) => boolean | undefined },
48
- onUpdate: (value: V) => void
58
+ as: Account,
59
+ depth: Depth & DepthsIn<V>,
60
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
61
+ ): () => void;
62
+ subscribe<V extends Value, Depth>(
63
+ this: ClassOf<V>,
64
+ existing: V,
65
+ depth: Depth & DepthsIn<V>,
66
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
49
67
  ): () => void;
50
68
 
51
- /** @category Subscription */
52
- subscribeEf<V extends Value>(
69
+ /** @category Subscription & Loading */
70
+ subscribeEf<V extends Value, Depth>(
53
71
  this: ClassOf<V>,
54
72
  id: ID<V>,
55
- options?: { require?: (value: V) => boolean | undefined }
56
- ): Stream.Stream<V, UnavailableError, AccountCtx>;
73
+ depth: Depth & DepthsIn<V>,
74
+ ): Stream.Stream<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx>;
57
75
  }
58
76
 
59
77
  /** @category Abstract interfaces */
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
79
  export interface CoValue<Type extends string = string, Raw = any> {
61
80
  /** @category Content */
62
81
  readonly id: ID<this>;
@@ -64,23 +83,24 @@ export interface CoValue<Type extends string = string, Raw = any> {
64
83
  _type: Type;
65
84
  /** @category Collaboration */
66
85
  _owner: Account | Group;
67
- /** @category Subscription & Loading */
68
- subscribe(listener: (update: this) => void, options?: RequireOptions<this>): () => void;
69
- /** @category Subscription & Loading */
70
- subscribeEf(): Stream.Stream<this, UnavailableError, never>;
71
86
  /** @category Internals */
72
87
  _raw: Raw;
73
88
  /** @internal */
74
- readonly _loadedAs: Account & Me;
89
+ readonly _loadedAs: Account;
75
90
  /** @category Stringifying & Inspection */
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
92
  toJSON(): any[] | object;
77
93
  /** @category Stringifying & Inspection */
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
95
  [inspect](): any;
79
96
  }
80
97
 
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
99
  export function isCoValue(value: any): value is CoValue {
82
100
  return value && value._type !== undefined;
83
101
  }
102
+
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
104
  export function isCoValueClass(value: any): value is CoValueClass {
85
105
  return typeof value === "function" && value.fromRaw !== undefined;
86
106
  }
@@ -117,6 +137,7 @@ export class CoValueBase implements CoValue {
117
137
  return Account.fromNode(this._raw.core.node);
118
138
  }
119
139
 
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
141
  constructor(..._args: any) {
121
142
  Object.defineProperty(this, "_instanceID", {
122
143
  value: `instance-${Math.random().toString(36).slice(2)}`,
@@ -130,108 +151,149 @@ export class CoValueBase implements CoValue {
130
151
  }
131
152
 
132
153
  /** @category Subscription & Loading */
133
- static loadEf<V extends CoValue>(
154
+ static load<V extends CoValue, Depth>(
134
155
  this: ClassOf<V> & typeof CoValueBase,
135
- id: ID<V>
136
- ): Effect.Effect<V, UnavailableError, AccountCtx> {
137
- return Effect.gen(this, function* (_) {
138
- const account = yield* _(AccountCtx);
139
- return yield* _(
140
- new Ref(id as ID<V>, account, this as CoValueClass<V>).loadEf()
141
- );
142
- });
156
+ id: ID<V>,
157
+ as: Account,
158
+ depth: Depth & DepthsIn<V>,
159
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
160
+ /** @category Subscription & Loading */
161
+ static load<V extends CoValue, Depth>(
162
+ this: ClassOf<V> & typeof CoValueBase,
163
+ existing: V,
164
+ depth: Depth & DepthsIn<V>,
165
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
166
+ static load<V extends CoValue, Depth>(
167
+ this: ClassOf<V> & typeof CoValueBase,
168
+
169
+ ...args:
170
+ | [ID<V>, Account, Depth & DepthsIn<V>]
171
+ | [V, Depth & DepthsIn<V>]
172
+ ): Promise<DeeplyLoaded<V, Depth> | undefined> {
173
+ const { id, as, depth } =
174
+ args.length === 3
175
+ ? { id: args[0], as: args[1], depth: args[2] }
176
+ : { id: args[0].id, as: args[0]._loadedAs, depth: args[1] };
177
+ return Effect.runPromise(
178
+ this.loadEf(id, depth).pipe(
179
+ Effect.mapError(() => undefined),
180
+ Effect.merge,
181
+ Effect.provideService(AccountCtx, as),
182
+ ),
183
+ );
143
184
  }
144
185
 
145
186
  /** @category Subscription & Loading */
146
- static load<V extends CoValue>(
187
+ static loadEf<V extends CoValue, Depth>(
147
188
  this: ClassOf<V> & typeof CoValueBase,
148
189
  id: ID<V>,
149
- options: {
150
- as: Account & Me;
151
- onProgress?: (progress: number) => void;
152
- }
153
- ): Promise<V | undefined> {
154
- return new Ref(id as ID<V>, options.as, this as CoValueClass<V>).load(
155
- options?.onProgress && { onProgress: options.onProgress }
190
+ depth: Depth & DepthsIn<V>,
191
+ ): Effect.Effect<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx> {
192
+ return this.subscribeEf(id, depth).pipe(
193
+ Stream.runHead,
194
+ Effect.andThen(
195
+ Effect.mapError((_noSuchElem) => "unavailable" as const),
196
+ ),
156
197
  );
157
198
  }
158
199
 
159
200
  /** @category Subscription & Loading */
160
- static subscribe<V extends CoValue, Acc extends Account>(
201
+ static subscribe<V extends CoValue, Depth>(
161
202
  this: ClassOf<V> & typeof CoValueBase,
162
203
  id: ID<V>,
163
- options: { as: Acc & Me; require?: (value: V) => boolean | undefined },
164
- onUpdate: (value: V) => void
204
+ as: Account,
205
+ depth: Depth & DepthsIn<V>,
206
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
207
+ ): () => void;
208
+ static subscribe<V extends CoValue, Depth>(
209
+ this: ClassOf<V> & typeof CoValueBase,
210
+ existing: V,
211
+ depth: Depth & DepthsIn<V>,
212
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
213
+ ): () => void;
214
+ static subscribe<V extends CoValue, Depth>(
215
+ this: ClassOf<V> & typeof CoValueBase,
216
+ ...args:
217
+ | [
218
+ ID<V>,
219
+ Account,
220
+ Depth & DepthsIn<V>,
221
+ (value: DeeplyLoaded<V, Depth>) => void,
222
+ ]
223
+ | [V, Depth & DepthsIn<V>, (value: DeeplyLoaded<V, Depth>) => void]
165
224
  ): () => void {
225
+ const { id, as, depth, listener } =
226
+ args.length === 4
227
+ ? {
228
+ id: args[0],
229
+ as: args[1],
230
+ depth: args[2],
231
+ listener: args[3],
232
+ }
233
+ : {
234
+ id: args[0].id,
235
+ as: args[0]._loadedAs,
236
+ depth: args[1],
237
+ listener: args[2],
238
+ };
166
239
  void Effect.runPromise(
167
240
  Effect.provideService(
168
- this.subscribeEf(id, { require: options.require }).pipe(
241
+ this.subscribeEf(id, depth).pipe(
169
242
  Stream.run(
170
243
  Sink.forEach((update) =>
171
- Effect.sync(() => onUpdate(update))
172
- )
173
- )
244
+ Effect.sync(() => listener(update)),
245
+ ),
246
+ ),
174
247
  ),
175
248
  AccountCtx,
176
- options.as as Account & Me
177
- )
249
+ as,
250
+ ),
178
251
  );
179
252
 
180
253
  return function unsubscribe() {};
181
254
  }
182
255
 
183
256
  /** @category Subscription & Loading */
184
- static subscribeEf<V extends CoValue>(
257
+ static subscribeEf<V extends CoValue, Depth>(
185
258
  this: ClassOf<V> & typeof CoValueBase,
186
259
  id: ID<V>,
187
- options?: { require?: (value: V) => boolean | undefined }
188
- ): Stream.Stream<V, UnavailableError, AccountCtx> {
189
- return Stream.fromEffect(this.loadEf(id)).pipe(
190
- Stream.flatMap((value) =>
260
+ depth: Depth & DepthsIn<V>,
261
+ ): Stream.Stream<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx> {
262
+ return AccountCtx.pipe(
263
+ Effect.andThen((account) =>
264
+ new Ref(id, account, this as CoValueClass<V>).loadEf(),
265
+ ),
266
+ Stream.fromEffect,
267
+ Stream.flatMap((value: V) =>
191
268
  Stream.asyncScoped<V, UnavailableError>((emit) =>
192
269
  Effect.gen(this, function* (_) {
193
270
  const subscription = new SubscriptionScope(
194
271
  value,
195
272
  this,
196
- (update) => {
197
- if (!options?.require || options.require(update)) {
198
- void emit.single(update as V);
199
- }
200
- }
273
+ (update) => void emit.single(update as V),
201
274
  );
202
275
 
203
276
  yield* _(
204
277
  Effect.addFinalizer(() =>
205
- Effect.sync(() => subscription.unsubscribeAll())
206
- )
278
+ Effect.sync(() =>
279
+ subscription.unsubscribeAll(),
280
+ ),
281
+ ),
207
282
  );
208
- })
209
- )
210
- )
211
- );
212
- }
213
-
214
- /** @category Subscription & Loading */
215
- subscribe(listener: (update: this) => void, options?: RequireOptions<this>): () => void {
216
- return (this.constructor as unknown as typeof CoValueBase).subscribe(
217
- this.id as unknown as ID<CoValue>,
218
- { as: this._loadedAs, require: options?.require as any },
219
- listener as unknown as (update: CoValue) => void
220
- );
221
- }
222
-
223
- /** @category Subscription & Loading */
224
- subscribeEf(options?: RequireOptions<this>): Stream.Stream<this, UnavailableError, never> {
225
- return Stream.provideService(
226
- (this.constructor as unknown as typeof CoValueBase).subscribeEf(
227
- this.id as unknown as ID<CoValue>,
228
- options as any
283
+ }),
284
+ ),
285
+ ),
286
+ Stream.filterMap((update: V) =>
287
+ Option.fromNullable(
288
+ fulfillsDepth(depth, update)
289
+ ? (update as DeeplyLoaded<V, Depth>)
290
+ : undefined,
291
+ ),
229
292
  ),
230
- AccountCtx,
231
- this._loadedAs
232
- ) as unknown as Stream.Stream<this, UnavailableError, never>;
293
+ );
233
294
  }
234
295
 
296
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
235
297
  toJSON(): object | any[] {
236
298
  return {
237
299
  id: this.id,
@@ -254,7 +316,3 @@ export class CoValueBase implements CoValue {
254
316
  return cast;
255
317
  }
256
318
  }
257
-
258
- export type RequireOptions<V extends CoValue> = {
259
- require?: (value: V) => boolean | undefined
260
- };
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import { ItemsSym } from "./symbols.js";
2
3
 
3
4
  (globalThis as any).devtoolsFormatters = [