jazz-tools 0.7.0-alpha.9 → 0.7.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/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +14 -15
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +331 -27
  7. package/LICENSE.txt +1 -1
  8. package/README.md +10 -2
  9. package/dist/coValues/account.js +86 -41
  10. package/dist/coValues/account.js.map +1 -1
  11. package/dist/coValues/coList.js +75 -48
  12. package/dist/coValues/coList.js.map +1 -1
  13. package/dist/coValues/coMap.js +167 -44
  14. package/dist/coValues/coMap.js.map +1 -1
  15. package/dist/coValues/coStream.js +192 -35
  16. package/dist/coValues/coStream.js.map +1 -1
  17. package/dist/coValues/deepLoading.js +60 -0
  18. package/dist/coValues/deepLoading.js.map +1 -0
  19. package/dist/coValues/extensions/imageDef.js +10 -7
  20. package/dist/coValues/extensions/imageDef.js.map +1 -1
  21. package/dist/coValues/group.js +73 -13
  22. package/dist/coValues/group.js.map +1 -1
  23. package/dist/coValues/interfaces.js +58 -35
  24. package/dist/coValues/interfaces.js.map +1 -1
  25. package/dist/implementation/devtoolsFormatters.js +114 -0
  26. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  27. package/dist/implementation/refs.js +58 -18
  28. package/dist/implementation/refs.js.map +1 -1
  29. package/dist/implementation/schema.js +58 -0
  30. package/dist/implementation/schema.js.map +1 -0
  31. package/dist/implementation/subscriptionScope.js +19 -1
  32. package/dist/implementation/subscriptionScope.js.map +1 -1
  33. package/dist/implementation/symbols.js +5 -0
  34. package/dist/implementation/symbols.js.map +1 -0
  35. package/dist/index.js +4 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/internal.js +5 -2
  38. package/dist/internal.js.map +1 -1
  39. package/dist/tests/coList.test.js +51 -48
  40. package/dist/tests/coList.test.js.map +1 -1
  41. package/dist/tests/coMap.test.js +131 -74
  42. package/dist/tests/coMap.test.js.map +1 -1
  43. package/dist/tests/coStream.test.js +56 -41
  44. package/dist/tests/coStream.test.js.map +1 -1
  45. package/dist/tests/deepLoading.test.js +188 -0
  46. package/dist/tests/deepLoading.test.js.map +1 -0
  47. package/dist/tests/groupsAndAccounts.test.js +83 -0
  48. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  49. package/package.json +17 -9
  50. package/src/coValues/account.ts +186 -128
  51. package/src/coValues/coList.ts +156 -107
  52. package/src/coValues/coMap.ts +272 -148
  53. package/src/coValues/coStream.ts +388 -87
  54. package/src/coValues/deepLoading.ts +229 -0
  55. package/src/coValues/extensions/imageDef.ts +17 -13
  56. package/src/coValues/group.ts +166 -59
  57. package/src/coValues/interfaces.ts +189 -160
  58. package/src/implementation/devtoolsFormatters.ts +110 -0
  59. package/src/implementation/inspect.ts +1 -1
  60. package/src/implementation/refs.ts +80 -28
  61. package/src/implementation/schema.ts +141 -0
  62. package/src/implementation/subscriptionScope.ts +48 -12
  63. package/src/implementation/symbols.ts +11 -0
  64. package/src/index.ts +19 -8
  65. package/src/internal.ts +7 -3
  66. package/src/tests/coList.test.ts +77 -62
  67. package/src/tests/coMap.test.ts +201 -114
  68. package/src/tests/coStream.test.ts +113 -84
  69. package/src/tests/deepLoading.test.ts +304 -0
  70. package/src/tests/groupsAndAccounts.test.ts +91 -0
  71. package/dist/implementation/encoding.js +0 -26
  72. package/dist/implementation/encoding.js.map +0 -1
  73. package/src/implementation/encoding.ts +0 -105
@@ -1,7 +1,7 @@
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 { DeeplyLoaded, DepthsIn, UnavailableError } from "../internal.js";
5
5
  import {
6
6
  Account,
7
7
  AccountCtx,
@@ -9,209 +9,238 @@ import {
9
9
  SubscriptionScope,
10
10
  Ref,
11
11
  inspect,
12
+ subscriptionsScopes,
12
13
  } from "../internal.js";
14
+ import { fulfillsDepth } from "./deepLoading.js";
13
15
 
14
- export type SubclassedConstructor<T> = {
15
- new (...args: any[]): T;
16
- };
17
-
18
- export interface CoValueClass<Value extends CoValue = CoValue, Init = any> {
19
- /** @category Construction and loading */
20
- new (init: Init, options: { owner: Account | Group }): Value;
21
-
16
+ /** @category Abstract interfaces */
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ export interface CoValueClass<Value extends CoValue = CoValue> {
22
19
  /** @ignore */
23
- fromRaw(raw: Value["_raw"]): Value;
24
-
25
- /** @category Construction and loading */
26
- load<V extends Value>(
27
- this: SubclassedConstructor<V>,
28
- id: ID<V>,
29
- options: {
30
- as: Account & Me;
31
- onProgress?: (progress: number) => void;
32
- }
33
- ): Promise<V | undefined>;
34
-
35
- /** @category Construction and loading */
36
- loadEf<V extends Value>(
37
- this: SubclassedConstructor<V>,
38
- id: ID<V>
39
- ): Effect.Effect<V, UnavailableError, AccountCtx>;
40
-
41
- /** @category Subscription */
42
- subscribe<V extends Value, Acc extends Account>(
43
- this: SubclassedConstructor<V>,
44
- id: ID<V>,
45
- options: { as: Acc & Me },
46
- onUpdate: (value: V) => void
47
- ): () => void;
48
-
49
- /** @category Subscription */
50
- subscribeEf<V extends Value>(
51
- this: SubclassedConstructor<V>,
52
- id: ID<V>
53
- ): Stream.Stream<V, UnavailableError, AccountCtx>;
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ new (...args: any[]): Value;
54
22
  }
55
23
 
56
- /** @category Schemas & CoValues - Abstract interfaces */
57
- export interface CoValue<Type extends string = string, Raw = any> {
58
- /** @category Value identity */
24
+ export interface CoValueFromRaw<V extends CoValue> {
25
+ fromRaw(raw: V["_raw"]): V;
26
+ }
27
+
28
+ /** @category Abstract interfaces */
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ export interface CoValue {
31
+ /** @category Content */
59
32
  readonly id: ID<this>;
60
- /** @category Value identity */
61
- _type: Type;
33
+ /** @category Type Helpers */
34
+ _type: string;
62
35
  /** @category Collaboration */
63
36
  _owner: Account | Group;
64
- /** @category Subscription */
65
- subscribe(listener: (update: this) => void): () => void;
66
- /** @category Subscription */
67
- subscribeEf(): Stream.Stream<this, UnavailableError, never>;
68
- /** @category Internals */
69
- _raw: Raw;
70
37
  /** @category Internals */
71
- readonly _loadedAs: Account & Me;
72
- /** @category Stringifying & inspection */
38
+ _raw: RawCoValue;
39
+ /** @internal */
40
+ readonly _loadedAs: Account;
41
+ /** @category Stringifying & Inspection */
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
43
  toJSON(): any[] | object;
74
- /** @category Stringifying & inspection */
44
+ /** @category Stringifying & Inspection */
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
46
  [inspect](): any;
76
47
  }
77
48
 
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
50
  export function isCoValue(value: any): value is CoValue {
79
51
  return value && value._type !== undefined;
80
52
  }
81
53
 
82
- /** @category Schemas & CoValues - Abstract interfaces */
83
- export type ID<T> = CojsonInternalTypes.RawCoID & {
84
- readonly __type: T;
85
- };
54
+ export function isCoValueClass<V extends CoValue>(
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ value: any,
57
+ ): value is CoValueClass<V> & CoValueFromRaw<V> {
58
+ return typeof value === "function" && value.fromRaw !== undefined;
59
+ }
86
60
 
61
+ /** @category CoValues */
62
+ export type ID<T> = CojsonInternalTypes.RawCoID & IDMarker<T>;
63
+
64
+ type IDMarker<out T> = { __type(_: never): T };
65
+
66
+ /** @internal */
87
67
  export class CoValueBase implements CoValue {
88
68
  id!: ID<this>;
89
69
  _type!: string;
90
70
  _raw!: RawCoValue;
71
+ _instanceID!: string;
91
72
 
92
73
  get _owner(): Account | Group {
93
- return this._raw.group instanceof RawAccount
94
- ? Account.fromRaw(this._raw.group)
95
- : Group.fromRaw(this._raw.group);
74
+ const owner =
75
+ this._raw.group instanceof RawAccount
76
+ ? Account.fromRaw(this._raw.group)
77
+ : Group.fromRaw(this._raw.group);
78
+
79
+ const subScope = subscriptionsScopes.get(this);
80
+ if (subScope) {
81
+ subScope.onRefAccessedOrSet(this.id, owner.id);
82
+ subscriptionsScopes.set(owner, subScope);
83
+ }
84
+
85
+ return owner;
96
86
  }
97
87
 
88
+ /** @private */
98
89
  get _loadedAs() {
99
90
  return Account.fromNode(this._raw.core.node);
100
91
  }
101
92
 
102
- constructor(..._args: any) {}
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ constructor(..._args: any) {
95
+ Object.defineProperty(this, "_instanceID", {
96
+ value: `instance-${Math.random().toString(36).slice(2)}`,
97
+ enumerable: false,
98
+ });
99
+ }
103
100
 
101
+ /** @category Internals */
104
102
  static fromRaw<V extends CoValue>(
105
- this: SubclassedConstructor<V>,
106
- raw: RawCoValue
103
+ this: CoValueClass<V>,
104
+ raw: RawCoValue,
107
105
  ): V {
108
- return new this(undefined, { fromRaw: raw });
106
+ return new this({ fromRaw: raw });
109
107
  }
110
108
 
111
- static loadEf<V extends CoValue>(
112
- this: SubclassedConstructor<V> & typeof CoValueBase,
113
- id: ID<V>
114
- ): Effect.Effect<V, UnavailableError, AccountCtx> {
115
- return Effect.gen(this, function* (_) {
116
- const account = yield* _(AccountCtx);
117
- return yield* _(
118
- new Ref(id as ID<V>, account, {
119
- ref: () => this as CoValueClass<V>,
120
- }).loadEf()
121
- );
122
- });
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ toJSON(): object | any[] {
111
+ return {
112
+ id: this.id,
113
+ type: this._type,
114
+ error: "unknown CoValue class",
115
+ };
123
116
  }
124
117
 
125
- static load<V extends CoValue>(
126
- this: SubclassedConstructor<V> & typeof CoValueBase,
127
- id: ID<V>,
128
- options: {
129
- as: Account & Me;
130
- onProgress?: (progress: number) => void;
131
- }
132
- ): Promise<V | undefined> {
133
- return new Ref(id as ID<V>, options.as, {
134
- ref: () => this as CoValueClass<V>,
135
- }).load(options?.onProgress && { onProgress: options.onProgress });
118
+ [inspect]() {
119
+ return this.toJSON();
136
120
  }
137
121
 
138
- static subscribe<V extends CoValue, Acc extends Account>(
139
- this: SubclassedConstructor<V> & typeof CoValueBase,
140
- id: ID<V>,
141
- options: { as: Acc & Me },
142
- onUpdate: (value: V) => void
143
- ): () => void {
144
- void Effect.runPromise(
145
- Effect.provideService(
146
- this.subscribeEf(id).pipe(
147
- Stream.run(
148
- Sink.forEach((update) =>
149
- Effect.sync(() => onUpdate(update))
150
- )
151
- )
152
- ),
153
- AccountCtx,
154
- options.as as Account & Me
155
- )
156
- );
157
-
158
- return function unsubscribe() {};
122
+ /** @category Type Helpers */
123
+ castAs<Cl extends CoValueClass & CoValueFromRaw<CoValue>>(
124
+ cl: Cl,
125
+ ): InstanceType<Cl> {
126
+ return cl.fromRaw(this._raw) as InstanceType<Cl>;
159
127
  }
128
+ }
160
129
 
161
- static subscribeEf<V extends CoValue>(
162
- this: SubclassedConstructor<V> & typeof CoValueBase,
163
- id: ID<V>
164
- ): Stream.Stream<V, UnavailableError, AccountCtx> {
165
- return Stream.fromEffect(this.loadEf(id)).pipe(
166
- Stream.flatMap((value) =>
167
- Stream.asyncScoped<V, UnavailableError>((emit) =>
168
- Effect.gen(this, function* (_) {
169
- const subscription = new SubscriptionScope(
170
- value,
171
- this,
172
- (update) => {
173
- void emit.single(update as V);
174
- }
175
- );
176
-
177
- yield* _(
178
- Effect.addFinalizer(() =>
179
- Effect.sync(() => subscription.unsubscribeAll())
180
- )
181
- );
182
- })
183
- )
184
- )
185
- );
186
- }
130
+ export function loadCoValue<V extends CoValue, Depth>(
131
+ cls: CoValueClass<V>,
132
+ id: ID<V>,
133
+ as: Account,
134
+ depth: Depth & DepthsIn<V>,
135
+ ): Promise<DeeplyLoaded<V, Depth> | undefined> {
136
+ return Effect.runPromise(
137
+ loadCoValueEf(cls, id, depth).pipe(
138
+ Effect.mapError(() => undefined),
139
+ Effect.merge,
140
+ Effect.provideService(AccountCtx, as),
141
+ ),
142
+ );
143
+ }
187
144
 
188
- subscribe(listener: (update: this) => void): () => void {
189
- return (this.constructor as unknown as typeof CoValueBase).subscribe(
190
- this.id as unknown as ID<CoValue>,
191
- { as: this._loadedAs },
192
- listener as unknown as (update: CoValue) => void
193
- );
194
- }
145
+ export function ensureCoValueLoaded<V extends CoValue, Depth>(
146
+ existing: V,
147
+ depth: Depth & DepthsIn<V>,
148
+ ): Promise<DeeplyLoaded<V, Depth> | undefined> {
149
+ return loadCoValue(
150
+ existing.constructor as CoValueClass<V>,
151
+ existing.id,
152
+ existing._loadedAs,
153
+ depth,
154
+ );
155
+ }
195
156
 
196
- subscribeEf(): Stream.Stream<this, UnavailableError, never> {
197
- return Stream.provideService(
198
- (this.constructor as unknown as typeof CoValueBase).subscribeEf(
199
- this.id as unknown as ID<CoValue>
157
+ export function loadCoValueEf<V extends CoValue, Depth>(
158
+ cls: CoValueClass<V>,
159
+ id: ID<V>,
160
+ depth: Depth & DepthsIn<V>,
161
+ ): Effect.Effect<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx> {
162
+ return subscribeToCoValueEf(cls, id, depth).pipe(
163
+ Stream.runHead,
164
+ Effect.andThen(
165
+ Effect.mapError((_noSuchElem) => "unavailable" as const),
166
+ ),
167
+ );
168
+ }
169
+
170
+ export function subscribeToCoValue<V extends CoValue, Depth>(
171
+ cls: CoValueClass<V>,
172
+ id: ID<V>,
173
+ as: Account,
174
+ depth: Depth & DepthsIn<V>,
175
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
176
+ ): () => void {
177
+ void Effect.runPromise(
178
+ Effect.provideService(
179
+ subscribeToCoValueEf(cls, id, depth).pipe(
180
+ Stream.run(
181
+ Sink.forEach((update) =>
182
+ Effect.sync(() => listener(update)),
183
+ ),
184
+ ),
200
185
  ),
201
186
  AccountCtx,
202
- this._loadedAs
203
- ) as unknown as Stream.Stream<this, UnavailableError, never>;
204
- }
187
+ as,
188
+ ),
189
+ );
205
190
 
206
- toJSON(): object | any[] {
207
- return {
208
- id: this.id,
209
- type: this._type,
210
- error: "unknown CoValue class",
211
- };
212
- }
191
+ return function unsubscribe() {};
192
+ }
213
193
 
214
- [inspect]() {
215
- return this.toJSON();
216
- }
194
+ export function subscribeToExistingCoValue<V extends CoValue, Depth>(
195
+ existing: V,
196
+ depth: Depth & DepthsIn<V>,
197
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
198
+ ): () => void {
199
+ return subscribeToCoValue(
200
+ existing.constructor as CoValueClass<V>,
201
+ existing.id,
202
+ existing._loadedAs,
203
+ depth,
204
+ listener,
205
+ );
206
+ }
207
+
208
+ export function subscribeToCoValueEf<V extends CoValue, Depth>(
209
+ cls: CoValueClass<V>,
210
+ id: ID<V>,
211
+ depth: Depth & DepthsIn<V>,
212
+ ): Stream.Stream<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx> {
213
+ return AccountCtx.pipe(
214
+ Effect.andThen((account) =>
215
+ new Ref(id, account, {
216
+ ref: cls,
217
+ optional: false,
218
+ }).loadEf(),
219
+ ),
220
+ Stream.fromEffect,
221
+ Stream.flatMap((value: V) =>
222
+ Stream.asyncScoped<V, UnavailableError>((emit) =>
223
+ Effect.gen(function* (_) {
224
+ const subscription = new SubscriptionScope(
225
+ value,
226
+ cls as CoValueClass<V> & CoValueFromRaw<V>,
227
+ (update) => void emit.single(update as V),
228
+ );
229
+
230
+ yield* _(
231
+ Effect.addFinalizer(() =>
232
+ Effect.sync(() => subscription.unsubscribeAll()),
233
+ ),
234
+ );
235
+ }),
236
+ ),
237
+ ),
238
+ Stream.filterMap((update: V) =>
239
+ Option.fromNullable(
240
+ fulfillsDepth(depth, update)
241
+ ? (update as DeeplyLoaded<V, Depth>)
242
+ : undefined,
243
+ ),
244
+ ),
245
+ );
217
246
  }
@@ -0,0 +1,110 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { ItemsSym } from "./symbols.js";
3
+
4
+ (globalThis as any).devtoolsFormatters = [
5
+ {
6
+ header: (object: any) => {
7
+ if (object._type === "CoMap") {
8
+ return ["div", {}, ["span", {}, object.constructor.name]];
9
+ } else if (object._type === "CoList") {
10
+ return [
11
+ "div",
12
+ {},
13
+ [
14
+ "span",
15
+ {},
16
+ object.constructor.name + "(" + object.length + ") ",
17
+ ],
18
+ ];
19
+ } else if (object._type === "Account") {
20
+ return [
21
+ "div",
22
+ {},
23
+ [
24
+ "span",
25
+ {},
26
+ object.constructor.name +
27
+ "(" +
28
+ object._refs.profile.value?.name +
29
+ (object.isMe ? " ME" : "") +
30
+ ")",
31
+ ],
32
+ ];
33
+ } else {
34
+ return null;
35
+ }
36
+ },
37
+ hasBody: function () {
38
+ return true;
39
+ },
40
+ body: function (object: any) {
41
+ if (object._type === "CoMap" || object._type === "Account") {
42
+ return [
43
+ "div",
44
+ { style: "margin-left: 15px" },
45
+ ["div", "id: ", ["object", { object: object.id }]],
46
+ ...Object.entries(object).map(([k, v]) => [
47
+ "div",
48
+ { style: "white-space: nowrap;" },
49
+ [
50
+ "span",
51
+ { style: "font-weight: bold; opacity: 0.6" },
52
+ k,
53
+ ": ",
54
+ ],
55
+ ["object", { object: v }],
56
+ ...(typeof object._schema[k] === "function"
57
+ ? v === null
58
+ ? [
59
+ [
60
+ "span",
61
+ { style: "opacity: 0.5" },
62
+ ` (pending ${object._schema[k].name} `,
63
+ [
64
+ "object",
65
+ { object: object._refs[k] },
66
+ ],
67
+ ")",
68
+ ],
69
+ ]
70
+ : []
71
+ : []),
72
+ ]),
73
+ ];
74
+ } else if (object._type === "CoList") {
75
+ return [
76
+ "div",
77
+ { style: "margin-left: 15px" },
78
+ ["div", "id: ", ["object", { object: object.id }]],
79
+ ...(object as any[]).map((v, i) => [
80
+ "div",
81
+ { style: "white-space: nowrap;" },
82
+ [
83
+ "span",
84
+ { style: "font-weight: bold; opacity: 0.6" },
85
+ i,
86
+ ": ",
87
+ ],
88
+ ["object", { object: v }],
89
+ ...(typeof object._schema[ItemsSym] === "function"
90
+ ? v === null
91
+ ? [
92
+ [
93
+ "span",
94
+ { style: "opacity: 0.5" },
95
+ ` (pending ${object._schema[ItemsSym].name} `,
96
+ [
97
+ "object",
98
+ { object: object._refs[i] },
99
+ ],
100
+ ")",
101
+ ],
102
+ ]
103
+ : []
104
+ : []),
105
+ ]),
106
+ ];
107
+ }
108
+ },
109
+ },
110
+ ];
@@ -1,2 +1,2 @@
1
1
  export const inspect = Symbol.for("nodejs.util.inspect.custom");
2
- export type inspect = typeof inspect;
2
+ export type inspect = typeof inspect;