jazz-tools 0.7.0-alpha.6 → 0.7.0-alpha.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/coValues/account.js +62 -29
  3. package/dist/coValues/account.js.map +1 -1
  4. package/dist/coValues/coList.js +139 -89
  5. package/dist/coValues/coList.js.map +1 -1
  6. package/dist/coValues/coMap.js +135 -151
  7. package/dist/coValues/coMap.js.map +1 -1
  8. package/dist/coValues/coStream.js +131 -57
  9. package/dist/coValues/coStream.js.map +1 -1
  10. package/dist/coValues/extensions/imageDef.js +10 -7
  11. package/dist/coValues/extensions/imageDef.js.map +1 -1
  12. package/dist/coValues/group.js +8 -30
  13. package/dist/coValues/group.js.map +1 -1
  14. package/dist/coValues/interfaces.js +6 -2
  15. package/dist/coValues/interfaces.js.map +1 -1
  16. package/dist/implementation/encoding.js +21 -0
  17. package/dist/implementation/encoding.js.map +1 -1
  18. package/dist/implementation/refs.js +10 -9
  19. package/dist/implementation/refs.js.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/tests/coList.test.js +5 -9
  23. package/dist/tests/coList.test.js.map +1 -1
  24. package/dist/tests/coMap.test.js +99 -36
  25. package/dist/tests/coMap.test.js.map +1 -1
  26. package/dist/tests/coStream.test.js +46 -51
  27. package/dist/tests/coStream.test.js.map +1 -1
  28. package/package.json +2 -2
  29. package/src/coValues/account.ts +82 -52
  30. package/src/coValues/coList.ts +170 -107
  31. package/src/coValues/coMap.ts +196 -230
  32. package/src/coValues/coStream.ts +169 -91
  33. package/src/coValues/extensions/imageDef.ts +7 -11
  34. package/src/coValues/group.ts +16 -63
  35. package/src/coValues/interfaces.ts +9 -8
  36. package/src/implementation/encoding.ts +55 -16
  37. package/src/implementation/refs.ts +12 -10
  38. package/src/index.ts +1 -1
  39. package/src/tests/coList.test.ts +5 -9
  40. package/src/tests/coMap.test.ts +77 -51
  41. package/src/tests/coStream.test.ts +61 -66
@@ -3,36 +3,38 @@ import type { Simplify } from "effect/Types";
3
3
  import { Schema } from "@effect/schema";
4
4
  import type {
5
5
  CoValue,
6
- Encoder,
7
6
  Encoding,
8
7
  EncodingFor,
9
8
  Group,
10
9
  ID,
11
10
  RefEncoded,
12
11
  EnsureCoValueNullable,
13
- CoValueClass,
12
+ IsVal,
14
13
  } from "../internal.js";
15
14
  import {
16
15
  Account,
17
16
  CoValueBase,
18
17
  Ref,
18
+ SchemaInit,
19
19
  inspect,
20
20
  makeRefs,
21
21
  subscriptionsScopes,
22
+ ItemsSym,
23
+ InitValues,
22
24
  } from "../internal.js";
23
25
 
24
- type ValidFields<Fields extends { [key: string]: any; _item?: any }> = {
25
- [Key in OwnKeys<Fields> as IfOptionalKey<
26
- Key,
27
- Fields
26
+ type ValidFields<Fields extends { [key: string]: any; [ItemsSym]?: any }> = {
27
+ [Key in keyof Fields & string as IsVal<
28
+ Fields[Key],
29
+ IfOptionalKey<Key, Fields>
28
30
  >]?: EnsureCoValueNullable<Fields[Key], Key>;
29
31
  } & {
30
- [Key in OwnKeys<Fields> as IfRequiredKey<
31
- Key,
32
- Fields
32
+ [Key in keyof Fields & string as IsVal<
33
+ Fields[Key],
34
+ IfRequiredKey<Key, Fields>
33
35
  >]: EnsureCoValueNullable<Fields[Key], Key>;
34
36
  } & {
35
- [Key in "_item"]?: EnsureCoValueNullable<Fields["_item"], Key>;
37
+ [Key in ItemsSym]?: EnsureCoValueNullable<Fields[ItemsSym], Key>;
36
38
  };
37
39
 
38
40
  type IfOptionalKey<Key extends keyof Obj, Obj> = Pick<
@@ -50,9 +52,30 @@ type IfRequiredKey<Key extends keyof Obj, Obj> = Pick<
50
52
 
51
53
  type DefaultFields = {
52
54
  [key: string]: any;
53
- _item?: any;
55
+ [ItemsSym]?: any;
54
56
  };
55
57
 
58
+ type CoMapEncoding<Fields extends object> = {
59
+ [Key in OwnKeys<Fields> as IsVal<Fields[Key], Key>]: EncodingFor<
60
+ Fields[Key]
61
+ >;
62
+ } &
63
+ {
64
+ [ItemsSym]: ItemsSym extends keyof Fields ? EncodingFor<Fields[ItemsSym]> : never;
65
+ }
66
+
67
+ type CoMapEdit<V> = {
68
+ value?: V;
69
+ ref?: V extends CoValue ? Ref<V> : never;
70
+ by?: Account;
71
+ madeAt: Date;
72
+ }
73
+
74
+ type InitValuesFor<C extends CoMap> = {
75
+ init: Simplify<CoMapInit<C>>;
76
+ owner: Account | Group;
77
+ }
78
+
56
79
  export class CoMap<Fields extends ValidFields<Fields> = DefaultFields>
57
80
  extends CoValueBase
58
81
  implements CoValue<"CoMap", RawCoMap>
@@ -65,14 +88,8 @@ export class CoMap<Fields extends ValidFields<Fields> = DefaultFields>
65
88
  _raw!: RawCoMap;
66
89
 
67
90
  static _encoding: any;
68
- get _encoding(): {
69
- [Key in OwnKeys<Fields>]: EncodingFor<Fields[Key]>;
70
- } & {
71
- _item: "_item" extends keyof Fields
72
- ? EncodingFor<Fields["_item"]>
73
- : never;
74
- } {
75
- return (this.constructor as typeof CoMap)._encoding;
91
+ get _encoding() {
92
+ return (this.constructor as typeof CoMap)._encoding as CoMapEncoding<this>
76
93
  }
77
94
 
78
95
  get _refs(): {
@@ -92,18 +109,11 @@ export class CoMap<Fields extends ValidFields<Fields> = DefaultFields>
92
109
  schema !== "json" && "ref" in schema;
93
110
  }) as OwnKeys<Fields>[],
94
111
  this._loadedAs,
95
- (key) => (this._encoding[key] as RefEncoded<CoValue>).ref()
112
+ (key) => this._encoding[key] as RefEncoded<CoValue>
96
113
  ) as any;
97
114
  }
98
115
 
99
- get _edits(): {
100
- [Key in OwnKeys<Fields>]: {
101
- value?: Fields[Key];
102
- ref?: Fields[Key] extends CoValue ? Ref<Fields[Key]> : never;
103
- by?: Account;
104
- madeAt: Date;
105
- };
106
- } {
116
+ get _edits() {
107
117
  return new Proxy(this, {
108
118
  get(target, key) {
109
119
  const rawEdit = target._raw.lastEditAt(key as string);
@@ -124,33 +134,35 @@ export class CoMap<Fields extends ValidFields<Fields> = DefaultFields>
124
134
  : new Ref(
125
135
  rawEdit.value as ID<CoValue>,
126
136
  target._loadedAs,
127
- descriptor.ref()
137
+ descriptor
128
138
  ).accessFrom(target),
129
139
  ref:
130
140
  descriptor !== "json" && "ref" in descriptor
131
141
  ? new Ref(
132
142
  rawEdit.value as ID<CoValue>,
133
143
  target._loadedAs,
134
- descriptor.ref()
144
+ descriptor
135
145
  )
136
146
  : undefined,
137
147
  by:
138
148
  rawEdit.by &&
139
- new Ref(
140
- rawEdit.by as ID<Account>,
141
- target._loadedAs,
142
- Account
143
- ).accessFrom(target),
149
+ new Ref(rawEdit.by as ID<Account>, target._loadedAs, {
150
+ ref: () => Account,
151
+ }).accessFrom(target),
144
152
  madeAt: rawEdit.at,
145
153
  };
146
154
  },
147
- }) as any;
155
+ }) as {
156
+ [Key in OwnKeys<this> as IsVal<this[Key], Key>]: CoMapEdit<this[Key]>
157
+ };
148
158
  }
149
159
 
150
160
  get _loadedAs() {
151
161
  return Account.fromNode(this._raw.core.node);
152
162
  }
153
163
 
164
+ [InitValues]?: InitValuesFor<this>;
165
+
154
166
  constructor(_init: undefined, options: { fromRaw: RawCoMap });
155
167
  constructor(
156
168
  init: Simplify<CoMapInit<Fields>>,
@@ -162,29 +174,21 @@ export class CoMap<Fields extends ValidFields<Fields> = DefaultFields>
162
174
  ) {
163
175
  super();
164
176
 
165
- if (!this._encoding) {
166
- throw new Error(
167
- "No schema found in " +
168
- this.constructor.name +
169
- " - ensure that you have a `static { this.define({...}) }` block in the class definition."
170
- );
177
+ if (init && "owner" in options) {
178
+ this[InitValues] = { init, owner: options.owner } as InitValuesFor<this>;
179
+ } else if ("fromRaw" in options) {
180
+ Object.defineProperties(this, {
181
+ id: {
182
+ value: options.fromRaw.id as unknown as ID<this>,
183
+ enumerable: false,
184
+ },
185
+ _raw: { value: options.fromRaw, enumerable: false },
186
+ });
187
+ } else {
188
+ throw new Error("Invalid CoMap constructor arguments");
171
189
  }
172
190
 
173
- const raw: RawCoMap = this.rawFromInit<Fields>(options, init);
174
-
175
- Object.defineProperties(this, {
176
- id: {
177
- value: raw.id,
178
- enumerable: false,
179
- },
180
- _raw: { value: raw, enumerable: false },
181
- });
182
-
183
- this.definePropertiesFromSchema();
184
-
185
- if (this._encoding._item) {
186
- return new Proxy(this, CoMapProxyHandler<Fields>());
187
- }
191
+ return new Proxy(this, CoMapProxyHandler as ProxyHandler<this>);
188
192
  }
189
193
 
190
194
  toJSON() {
@@ -214,144 +218,43 @@ export class CoMap<Fields extends ValidFields<Fields> = DefaultFields>
214
218
  }
215
219
 
216
220
  rawFromInit<Fields extends object = Record<string, any>>(
217
- options: { owner: Account | Group } | { fromRaw: RawCoMap },
218
- init: Simplify<CoMapInit<Fields>> | undefined
221
+ init: Simplify<CoMapInit<Fields>> | undefined,
222
+ owner: Account | Group
219
223
  ) {
220
- let raw: RawCoMap;
224
+ const rawOwner = owner._raw;
221
225
 
222
- if ("fromRaw" in options) {
223
- raw = options.fromRaw;
224
- } else {
225
- const rawOwner = options.owner._raw;
226
+ const rawInit = {} as {
227
+ [key in keyof Fields]: JsonValue | undefined;
228
+ };
226
229
 
227
- const rawInit = {} as {
228
- [key in keyof Fields]: JsonValue | undefined;
229
- };
230
+ if (init)
231
+ for (const key of Object.keys(init) as (keyof Fields)[]) {
232
+ const initValue = init[key as keyof typeof init];
230
233
 
231
- if (init)
232
- for (const key of Object.keys(init) as (keyof Fields)[]) {
233
- const initValue = init[key as keyof typeof init];
234
+ const descriptor = (this._encoding[
235
+ key as keyof typeof this._encoding
236
+ ] || this._encoding[ItemsSym]) as Encoding;
234
237
 
235
- const descriptor = (this._encoding[
236
- key as keyof typeof this._encoding
237
- ] || this._encoding._item) as Encoding;
238
-
239
- if (descriptor === "json") {
240
- rawInit[key] = initValue as JsonValue;
241
- } else if ("ref" in descriptor) {
242
- if (initValue) {
243
- rawInit[key] = (initValue as unknown as CoValue).id;
244
- }
245
- } else if ("encoded" in descriptor) {
246
- rawInit[key] = Schema.encodeSync(descriptor.encoded)(
247
- initValue as any
248
- );
238
+ if (descriptor === "json") {
239
+ rawInit[key] = initValue as JsonValue;
240
+ } else if ("ref" in descriptor) {
241
+ if (initValue) {
242
+ rawInit[key] = (initValue as unknown as CoValue).id;
249
243
  }
244
+ } else if ("encoded" in descriptor) {
245
+ rawInit[key] = Schema.encodeSync(descriptor.encoded)(
246
+ initValue as any
247
+ );
250
248
  }
251
-
252
- raw = rawOwner.createMap(rawInit);
253
- }
254
- return raw;
255
- }
256
-
257
- static encoding<V extends CoMap>(
258
- this: { new (...args: any): V } & typeof CoMap,
259
- fields: Simplify<{
260
- [Key in keyof V["_encoding"] as V["_encoding"][Key] extends never
261
- ? never
262
- : Key]: Simplify<V["_encoding"][Key]>;
263
- }>
264
- ) {
265
- this._encoding ||= {};
266
- Object.assign(this._encoding, fields);
267
- }
268
-
269
- private definePropertiesFromSchema() {
270
- for (const [key, fieldSchema] of Object.entries(this._encoding)) {
271
- if (key === "indexSignature") continue;
272
- const descriptor = fieldSchema as Encoding;
273
- if (descriptor === "json") {
274
- Object.defineProperty(
275
- this,
276
- key,
277
- this.primitivePropDef(key as string)
278
- );
279
- } else if ("encoded" in descriptor) {
280
- Object.defineProperty(
281
- this,
282
- key,
283
- this.encodedPropDef(key as string, descriptor.encoded)
284
- );
285
- } else if ("ref" in descriptor) {
286
- Object.defineProperty(
287
- this,
288
- key,
289
- this.refPropDef(
290
- key as string,
291
- (descriptor as RefEncoded<CoValue>).ref
292
- )
293
- );
294
249
  }
295
- }
296
- }
297
250
 
298
- private primitivePropDef(key: string): PropertyDescriptor {
299
- return {
300
- get: () => {
301
- return this._raw.get(key);
302
- },
303
- set(this: CoMap, value: JsonValue) {
304
- this._raw.set(key, value);
305
- },
306
- enumerable: true,
307
- configurable: true,
308
- };
309
- }
310
-
311
- private encodedPropDef(key: string, arg: Encoder<any>): PropertyDescriptor {
312
- return {
313
- get: () => {
314
- const raw = this._raw.get(key);
315
- return raw === undefined
316
- ? undefined
317
- : Schema.decodeSync(arg)(raw);
318
- },
319
- set(this: CoMap, value: unknown) {
320
- this._raw.set(key, Schema.encodeSync(arg)(value));
321
- },
322
- enumerable: true,
323
- configurable: true,
324
- };
325
- }
326
-
327
- private refPropDef(
328
- key: string,
329
- ref: () => CoValueClass<CoValue>
330
- ): PropertyDescriptor {
331
- return {
332
- get: () => {
333
- const rawID = this._raw.get(key);
334
- return rawID === undefined
335
- ? undefined
336
- : new Ref(
337
- rawID as unknown as ID<CoValue>,
338
- this._loadedAs,
339
- ref()
340
- ).accessFrom(this);
341
- },
342
- set: (value: CoValue) => {
343
- this._raw.set(key, value.id);
344
- subscriptionsScopes.get(this)?.onRefAccessedOrSet(value.id);
345
- },
346
- enumerable: true,
347
- configurable: true,
348
- };
251
+ return rawOwner.createMap(rawInit);
349
252
  }
350
253
  }
351
254
 
352
255
  export type OwnKeys<Fields extends object> = Exclude<
353
256
  keyof Fields & string,
354
- keyof CoMap<Record<string, never>> | `_${string}`
257
+ keyof CoMap<Record<string, never>>
355
258
  >;
356
259
 
357
260
  export type CoMapInit<Fields extends object> = {
@@ -359,19 +262,44 @@ export type CoMapInit<Fields extends object> = {
359
262
  ? never
360
263
  : null extends Fields[Key]
361
264
  ? never
362
- : Key]: Fields[Key];
363
- } & { [Key in OwnKeys<Fields>]?: Fields[Key] };
265
+ : IsVal<Fields[Key], Key>]: Fields[Key];
266
+ } & { [Key in OwnKeys<Fields> as IsVal<Fields[Key], Key>]?: Fields[Key] };
267
+
268
+ function tryInit(map: CoMap) {
269
+ if (
270
+ map[InitValues] &&
271
+ (map._encoding[ItemsSym] ||
272
+ Object.keys(map[InitValues].init).every(
273
+ (key) => (map._encoding as any)[key]
274
+ ))
275
+ ) {
276
+ const raw = map.rawFromInit(
277
+ map[InitValues].init,
278
+ map[InitValues].owner
279
+ );
280
+ Object.defineProperties(map, {
281
+ id: {
282
+ value: raw.id,
283
+ enumerable: false,
284
+ },
285
+ _raw: { value: raw, enumerable: false },
286
+ });
287
+ delete map[InitValues];
288
+ }
289
+ }
364
290
 
365
291
  // TODO: cache handlers per descriptor for performance?
366
- function CoMapProxyHandler<Fields extends ValidFields<Fields>>(): ProxyHandler<
367
- CoMap<Fields>
368
- > {
369
- return {
370
- get(target, key, receiver) {
371
- const descriptor = target._encoding._item as Encoding;
372
- if (key in target || typeof key === "symbol") {
373
- return Reflect.get(target, key, receiver);
374
- } else {
292
+ const CoMapProxyHandler: ProxyHandler<CoMap> = {
293
+ get(target, key, receiver) {
294
+ if (key === "_encoding") {
295
+ return Reflect.get(target, key);
296
+ } else if (key in target) {
297
+ return Reflect.get(target, key, receiver);
298
+ } else {
299
+ const descriptor = (target._encoding[
300
+ key as keyof CoMap["_encoding"]
301
+ ] || target._encoding[ItemsSym]) as Encoding;
302
+ if (descriptor && typeof key === "string") {
375
303
  const raw = target._raw.get(key);
376
304
 
377
305
  if (descriptor === "json") {
@@ -386,52 +314,90 @@ function CoMapProxyHandler<Fields extends ValidFields<Fields>>(): ProxyHandler<
386
314
  : new Ref(
387
315
  raw as unknown as ID<CoValue>,
388
316
  target._loadedAs,
389
- descriptor.ref()
390
- ).accessFrom(target);
317
+ descriptor
318
+ ).accessFrom(receiver);
391
319
  }
392
- }
393
- },
394
- set(target, key, value, receiver) {
395
- const descriptor = target._encoding._item as Encoding;
396
- if (key in target || typeof key === "symbol") {
397
- return Reflect.set(target, key, value, receiver);
398
320
  } else {
399
- if (descriptor === "json") {
400
- target._raw.set(key, value);
401
- } else if ("encoded" in descriptor) {
402
- target._raw.set(
403
- key,
404
- Schema.encodeSync(descriptor.encoded)(value)
405
- );
406
- } else if ("ref" in descriptor) {
407
- target._raw.set(key, value.id);
408
- subscriptionsScopes
409
- .get(target)
410
- ?.onRefAccessedOrSet(value.id);
411
- }
412
- return true;
321
+ return undefined;
413
322
  }
414
- },
415
- ownKeys(target) {
416
- const keys = Reflect.ownKeys(target).filter((k) => k !== "_item");
417
- for (const key of target._raw.keys()) {
418
- if (!keys.includes(key)) {
419
- keys.push(key);
420
- }
323
+ }
324
+ },
325
+ set(target, key, value, receiver) {
326
+ if (
327
+ (typeof key === "string" || ItemsSym) &&
328
+ typeof value === "object" &&
329
+ SchemaInit in value
330
+ ) {
331
+ (target.constructor as typeof CoMap)._encoding ||= {};
332
+ (target.constructor as typeof CoMap)._encoding[key] =
333
+ value[SchemaInit];
334
+ tryInit(target);
335
+ return true;
336
+ }
337
+
338
+ const descriptor = (target._encoding[key as keyof CoMap["_encoding"]] ||
339
+ target._encoding[ItemsSym]) as Encoding;
340
+ if (descriptor && typeof key === "string") {
341
+ if (descriptor === "json") {
342
+ target._raw.set(key, value);
343
+ } else if ("encoded" in descriptor) {
344
+ target._raw.set(
345
+ key,
346
+ Schema.encodeSync(descriptor.encoded)(value)
347
+ );
348
+ } else if ("ref" in descriptor) {
349
+ target._raw.set(key, value.id);
350
+ subscriptionsScopes.get(target)?.onRefAccessedOrSet(value.id);
421
351
  }
352
+ return true;
353
+ } else {
354
+ return Reflect.set(target, key, value, receiver);
355
+ }
356
+ },
357
+ defineProperty(target, key, attributes) {
358
+ if (
359
+ "value" in attributes &&
360
+ typeof attributes.value === "object" &&
361
+ SchemaInit in attributes.value
362
+ ) {
363
+ (target.constructor as typeof CoMap)._encoding ||= {};
364
+ (target.constructor as typeof CoMap)._encoding[key as string] =
365
+ attributes.value[SchemaInit];
366
+ tryInit(target);
367
+ return true;
368
+ } else {
369
+ return Reflect.defineProperty(target, key, attributes);
370
+ }
371
+ },
372
+ ownKeys(target) {
373
+ const keys = Reflect.ownKeys(target).filter((k) => k !== ItemsSym);
374
+ for (const key of Reflect.ownKeys(target._encoding)) {
375
+ if (key !== ItemsSym && !keys.includes(key)) {
376
+ keys.push(key);
377
+ }
378
+ }
379
+ for (const key of target._raw.keys()) {
380
+ if (!keys.includes(key)) {
381
+ keys.push(key);
382
+ }
383
+ }
422
384
 
423
- return keys;
424
- },
425
- getOwnPropertyDescriptor(target, key) {
426
- if (key in target) {
427
- return Reflect.getOwnPropertyDescriptor(target, key);
428
- } else if (key in target._raw.ops) {
385
+ return keys;
386
+ },
387
+ getOwnPropertyDescriptor(target, key) {
388
+ if (key in target) {
389
+ return Reflect.getOwnPropertyDescriptor(target, key);
390
+ } else {
391
+ const descriptor = (target._encoding[
392
+ key as keyof CoMap["_encoding"]
393
+ ] || target._encoding[ItemsSym]) as Encoding;
394
+ if (descriptor || key in target._raw.ops) {
429
395
  return {
430
396
  enumerable: true,
431
397
  configurable: true,
432
398
  writable: true,
433
399
  };
434
400
  }
435
- },
436
- };
437
- }
401
+ }
402
+ },
403
+ };