jazz-tools 0.7.0-alpha.3 → 0.7.0-alpha.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/.turbo/turbo-build.log +78 -8
  2. package/CHANGELOG.md +181 -0
  3. package/dist/coValues/account.js +81 -41
  4. package/dist/coValues/account.js.map +1 -1
  5. package/dist/coValues/coList.js +156 -104
  6. package/dist/coValues/coList.js.map +1 -1
  7. package/dist/coValues/coMap.js +182 -163
  8. package/dist/coValues/coMap.js.map +1 -1
  9. package/dist/coValues/coStream.js +202 -71
  10. package/dist/coValues/coStream.js.map +1 -1
  11. package/dist/coValues/extensions/imageDef.js +13 -8
  12. package/dist/coValues/extensions/imageDef.js.map +1 -1
  13. package/dist/coValues/group.js +46 -38
  14. package/dist/coValues/group.js.map +1 -1
  15. package/dist/coValues/interfaces.js +23 -5
  16. package/dist/coValues/interfaces.js.map +1 -1
  17. package/dist/implementation/refs.js +26 -12
  18. package/dist/implementation/refs.js.map +1 -1
  19. package/dist/implementation/schema.js +38 -1
  20. package/dist/implementation/schema.js.map +1 -1
  21. package/dist/implementation/symbols.js +5 -0
  22. package/dist/implementation/symbols.js.map +1 -0
  23. package/dist/index.js +4 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/internal.js +1 -0
  26. package/dist/internal.js.map +1 -1
  27. package/dist/tests/coList.test.js +32 -36
  28. package/dist/tests/coList.test.js.map +1 -1
  29. package/dist/tests/coMap.test.js +170 -59
  30. package/dist/tests/coMap.test.js.map +1 -1
  31. package/dist/tests/coStream.test.js +59 -64
  32. package/dist/tests/coStream.test.js.map +1 -1
  33. package/dist/tests/groupsAndAccounts.test.js +88 -0
  34. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  35. package/package.json +5 -4
  36. package/src/coValues/account.ts +129 -105
  37. package/src/coValues/coList.ts +200 -131
  38. package/src/coValues/coMap.ts +243 -305
  39. package/src/coValues/coStream.ts +300 -127
  40. package/src/coValues/extensions/imageDef.ts +14 -16
  41. package/src/coValues/group.ts +90 -106
  42. package/src/coValues/interfaces.ts +33 -12
  43. package/src/implementation/refs.ts +42 -25
  44. package/src/implementation/schema.ts +69 -46
  45. package/src/implementation/symbols.ts +12 -0
  46. package/src/index.ts +10 -8
  47. package/src/internal.ts +1 -0
  48. package/src/tests/coList.test.ts +35 -39
  49. package/src/tests/coMap.test.ts +176 -81
  50. package/src/tests/coStream.test.ts +76 -81
  51. package/src/tests/groupsAndAccounts.test.ts +100 -0
@@ -11,181 +11,151 @@ import type {
11
11
  import { cojsonInternals } from "cojson";
12
12
  import type {
13
13
  CoValue,
14
- EnsureItemNullable,
15
- FieldDescriptor,
16
- FieldDescriptorFor,
14
+ Schema,
15
+ SchemaFor,
17
16
  Group,
18
17
  ID,
19
18
  Me,
19
+ IfCo,
20
+ SubclassedConstructor,
21
+ UnCo,
20
22
  } from "../internal.js";
21
- import { Account, CoValueBase, ValueRef, inspect } from "../internal.js";
22
- import { Schema } from "@effect/schema";
23
+ import {
24
+ ItemsSym,
25
+ Account,
26
+ CoValueBase,
27
+ Ref,
28
+ inspect,
29
+ co,
30
+ InitValues,
31
+ SchemaInit,
32
+ isRefEncoded,
33
+ } from "../internal.js";
34
+ import { encodeSync, decodeSync } from "@effect/schema/Schema";
35
+
36
+ export type CoStreamEntry<Item> = SingleCoStreamEntry<Item> & {
37
+ all: IterableIterator<SingleCoStreamEntry<Item>>;
38
+ };
23
39
 
24
- export type CoStreamEntry<Item> = {
40
+ export type SingleCoStreamEntry<Item> = {
25
41
  value: NonNullable<Item> extends CoValue ? NonNullable<Item> | null : Item;
26
- ref?: NonNullable<Item> extends CoValue
27
- ? ValueRef<NonNullable<Item>>
28
- : never;
29
- by?: Account;
42
+ ref: NonNullable<Item> extends CoValue ? Ref<NonNullable<Item>> : never;
43
+ by?: Account | null;
30
44
  madeAt: Date;
31
45
  tx: CojsonInternalTypes.TransactionID;
32
46
  };
33
47
 
34
- export class CoStream<Item extends EnsureItemNullable<Item, "Co.Stream"> = any>
48
+ export class CoStream<Item = any>
35
49
  extends CoValueBase
36
50
  implements CoValue<"CoStream", RawCoStream>
37
51
  {
38
- id!: ID<this>;
39
- _type!: "CoStream";
52
+ static Of<Item>(item: IfCo<Item, Item>): typeof CoStream<Item> {
53
+ return class CoStreamOf extends CoStream<Item> {
54
+ [co.items] = item;
55
+ };
56
+ }
57
+
58
+ declare id: ID<this>;
59
+ declare _type: "CoStream";
40
60
  static {
41
61
  this.prototype._type = "CoStream";
42
62
  }
43
- _raw!: RawCoStream;
63
+ declare _raw: RawCoStream;
44
64
 
45
65
  /** @internal This is only a marker type and doesn't exist at runtime */
46
- _item!: Item;
47
- static _encoding: any;
48
- get _encoding(): {
49
- _item: FieldDescriptorFor<Item>;
66
+ [ItemsSym]!: Item;
67
+ static _schema: any;
68
+ get _schema(): {
69
+ [ItemsSym]: SchemaFor<Item>;
50
70
  } {
51
- return (this.constructor as typeof CoStream)._encoding;
71
+ return (this.constructor as typeof CoStream)._schema;
52
72
  }
53
73
 
54
- by: {
55
- [key: ID<Account>]: CoStreamEntry<Item>;
56
- } = {};
74
+ [key: ID<Account>]: CoStreamEntry<Item>;
75
+
57
76
  get byMe(): CoStreamEntry<Item> | undefined {
58
- return this.by[this._loadedAs.id];
77
+ return this[this._loadedAs.id];
59
78
  }
60
- in: {
79
+ perSession!: {
61
80
  [key: SessionID]: CoStreamEntry<Item>;
62
- } = {};
81
+ };
63
82
  get inCurrentSession(): CoStreamEntry<Item> | undefined {
64
- return this.in[this._loadedAs.sessionID];
83
+ return this.perSession[this._loadedAs.sessionID];
65
84
  }
66
85
 
67
- constructor(_init: undefined, options: { fromRaw: RawCoStream });
68
- constructor(init: Item[], options: { owner: Account | Group });
86
+ [InitValues]?: any;
87
+
69
88
  constructor(
70
- init: Item[] | undefined,
71
- options: { owner: Account | Group } | { fromRaw: RawCoStream }
89
+ options:
90
+ | { init: Item[]; owner: Account | Group }
91
+ | { fromRaw: RawCoStream }
72
92
  ) {
73
93
  super();
74
94
 
75
- let raw: RawCoStream;
76
-
77
95
  if ("fromRaw" in options) {
78
- raw = options.fromRaw;
96
+ Object.defineProperties(this, {
97
+ id: {
98
+ value: options.fromRaw.id,
99
+ enumerable: false,
100
+ },
101
+ _raw: { value: options.fromRaw, enumerable: false },
102
+ });
79
103
  } else {
80
- const rawOwner = options.owner._raw;
81
-
82
- raw = rawOwner.createStream();
83
- }
84
-
85
- Object.defineProperties(this, {
86
- id: {
87
- value: raw.id,
88
- enumerable: false,
89
- },
90
- _raw: { value: raw, enumerable: false },
91
- });
92
-
93
- if (init !== undefined) {
94
- for (const item of init) {
95
- this.pushItem(item);
96
- }
104
+ this[InitValues] = {
105
+ init: options.init,
106
+ owner: options.owner,
107
+ };
97
108
  }
98
109
 
99
- this.updateEntries();
110
+ return new Proxy(this, CoStreamProxyHandler as ProxyHandler<this>);
100
111
  }
101
112
 
102
- private updateEntries() {
103
- for (const accountID of this._raw.accounts()) {
104
- Object.defineProperty(this.by, accountID, {
105
- get: () => {
106
- const rawEntry = this._raw.lastItemBy(accountID);
107
-
108
- if (!rawEntry) return;
109
- return entryFromRawEntry(
110
- this,
111
- rawEntry,
112
- this._loadedAs,
113
- accountID as unknown as ID<Account>,
114
- this._encoding._item
115
- );
116
- },
117
- configurable: true,
118
- enumerable: true,
119
- });
120
- }
121
-
122
- for (const sessionID of this._raw.sessions()) {
123
- Object.defineProperty(this.in, sessionID, {
124
- get: () => {
125
- const rawEntry = this._raw.lastItemIn(
126
- sessionID as unknown as SessionID
127
- );
128
-
129
- if (!rawEntry) return;
130
- const by =
131
- cojsonInternals.accountOrAgentIDfromSessionID(
132
- sessionID
133
- );
134
- return entryFromRawEntry(
135
- this,
136
- rawEntry,
137
- this._loadedAs,
138
- cojsonInternals.isAccountID(by)
139
- ? (by as unknown as ID<Account>)
140
- : undefined,
141
- this._encoding._item
142
- );
143
- },
144
- configurable: true,
145
- enumerable: true,
146
- });
147
- }
113
+ static create<S extends CoStream>(
114
+ this: SubclassedConstructor<S>,
115
+ init: S extends CoStream<infer Item> ? UnCo<Item>[] : never,
116
+ options: { owner: Account | Group }
117
+ ) {
118
+ return new this({ init, owner: options.owner });
148
119
  }
149
120
 
150
121
  push(...items: Item[]) {
151
122
  for (const item of items) {
152
123
  this.pushItem(item);
153
124
  }
154
- this.updateEntries();
155
125
  }
156
126
 
157
127
  private pushItem(item: Item) {
158
- const itemDescriptor = this._encoding._item as FieldDescriptor;
128
+ const itemDescriptor = this._schema[ItemsSym] as Schema;
159
129
 
160
130
  if (itemDescriptor === "json") {
161
131
  this._raw.push(item as JsonValue);
162
132
  } else if ("encoded" in itemDescriptor) {
163
- this._raw.push(Schema.encodeSync(itemDescriptor.encoded)(item));
164
- } else if ("ref" in itemDescriptor) {
133
+ this._raw.push(encodeSync(itemDescriptor.encoded)(item));
134
+ } else if (isRefEncoded(itemDescriptor)) {
165
135
  this._raw.push((item as unknown as CoValue).id);
166
136
  }
167
137
  }
168
138
 
169
139
  toJSON() {
170
- const itemDescriptor = this._encoding._item as FieldDescriptor;
140
+ const itemDescriptor = this._schema[ItemsSym] as Schema;
171
141
  const mapper =
172
142
  itemDescriptor === "json"
173
143
  ? (v: unknown) => v
174
144
  : "encoded" in itemDescriptor
175
- ? Schema.encodeSync(itemDescriptor.encoded)
145
+ ? encodeSync(itemDescriptor.encoded)
176
146
  : (v: unknown) => v && (v as CoValue).id;
177
147
 
178
148
  return {
179
149
  id: this.id,
180
150
  _type: this._type,
181
- by: Object.fromEntries(
182
- Object.entries(this.by).map(([account, entry]) => [
151
+ ...Object.fromEntries(
152
+ Object.entries(this).map(([account, entry]) => [
183
153
  account,
184
154
  mapper(entry.value),
185
155
  ])
186
156
  ),
187
157
  in: Object.fromEntries(
188
- Object.entries(this.in).map(([session, entry]) => [
158
+ Object.entries(this.perSession).map(([session, entry]) => [
189
159
  session,
190
160
  mapper(entry.value),
191
161
  ])
@@ -197,12 +167,12 @@ export class CoStream<Item extends EnsureItemNullable<Item, "Co.Stream"> = any>
197
167
  return this.toJSON();
198
168
  }
199
169
 
200
- static encoding<V extends CoStream>(
170
+ static schema<V extends CoStream>(
201
171
  this: { new (...args: any): V } & typeof CoStream,
202
- def: { _item: V["_encoding"]["_item"] }
172
+ def: { [ItemsSym]: V["_schema"][ItemsSym] }
203
173
  ) {
204
- this._encoding ||= {};
205
- Object.assign(this._encoding, def);
174
+ this._schema ||= {};
175
+ Object.assign(this._schema, def);
206
176
  }
207
177
  }
208
178
 
@@ -216,32 +186,48 @@ function entryFromRawEntry<Item>(
216
186
  },
217
187
  loadedAs: Account & Me,
218
188
  accountID: ID<Account> | undefined,
219
- itemField: FieldDescriptor
220
- ) {
189
+ itemField: Schema
190
+ ): Omit<CoStreamEntry<Item>, "all"> {
221
191
  return {
222
- get value(): Item | undefined {
192
+ get value(): NonNullable<Item> extends CoValue
193
+ ? (CoValue & Item) | null
194
+ : Item {
223
195
  if (itemField === "json") {
224
- return rawEntry.value as Item;
196
+ return rawEntry.value as NonNullable<Item> extends CoValue
197
+ ? (CoValue & Item) | null
198
+ : Item;
225
199
  } else if ("encoded" in itemField) {
226
- return Schema.decodeSync(itemField.encoded)(rawEntry.value);
227
- } else if ("ref" in itemField) {
228
- return this.ref?.accessFrom(accessFrom) as Item;
200
+ return decodeSync(itemField.encoded)(rawEntry.value);
201
+ } else if (isRefEncoded(itemField)) {
202
+ return this.ref?.accessFrom(
203
+ accessFrom
204
+ ) as NonNullable<Item> extends CoValue
205
+ ? (CoValue & Item) | null
206
+ : Item;
207
+ } else {
208
+ throw new Error("Invalid item field schema");
229
209
  }
230
210
  },
231
- get ref() {
232
- if (itemField !== "json" && "ref" in itemField) {
211
+ get ref(): NonNullable<Item> extends CoValue
212
+ ? Ref<NonNullable<Item>>
213
+ : never {
214
+ if (itemField !== "json" && isRefEncoded(itemField)) {
233
215
  const rawId = rawEntry.value;
234
- return new ValueRef(
216
+ return new Ref(
235
217
  rawId as unknown as ID<CoValue>,
236
218
  loadedAs,
237
- itemField.ref()
238
- );
219
+ itemField
220
+ ) as NonNullable<Item> extends CoValue
221
+ ? Ref<NonNullable<Item>>
222
+ : never;
223
+ } else {
224
+ return undefined as never;
239
225
  }
240
226
  },
241
227
  get by() {
242
228
  return (
243
229
  accountID &&
244
- new ValueRef(
230
+ new Ref<Account>(
245
231
  accountID as unknown as ID<Account>,
246
232
  loadedAs,
247
233
  Account
@@ -253,16 +239,196 @@ function entryFromRawEntry<Item>(
253
239
  };
254
240
  }
255
241
 
242
+ function init(stream: CoStream) {
243
+ const init = stream[InitValues];
244
+ if (!init) return;
245
+
246
+ const raw = init.owner._raw.createStream();
247
+
248
+ Object.defineProperties(stream, {
249
+ id: {
250
+ value: raw.id,
251
+ enumerable: false,
252
+ },
253
+ _raw: { value: raw, enumerable: false },
254
+ });
255
+
256
+ if (init.init) {
257
+ stream.push(...init.init);
258
+ }
259
+
260
+ delete stream[InitValues];
261
+ }
262
+
263
+ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
264
+ get(target, key, receiver) {
265
+ if (typeof key === "string" && key.startsWith("co_")) {
266
+ const rawEntry = target._raw.lastItemBy(key as AccountID);
267
+
268
+ if (!rawEntry) return;
269
+ const entry = entryFromRawEntry(
270
+ receiver,
271
+ rawEntry,
272
+ target._loadedAs,
273
+ key as unknown as ID<Account>,
274
+ target._schema[ItemsSym]
275
+ );
276
+
277
+ Object.defineProperty(entry, "all", {
278
+ get: () => {
279
+ const allRawEntries = target._raw.itemsBy(key as AccountID);
280
+ return (function* () {
281
+ while (true) {
282
+ const rawEntry = allRawEntries.next();
283
+ if (rawEntry.done) return;
284
+ yield entryFromRawEntry(
285
+ receiver,
286
+ rawEntry.value,
287
+ target._loadedAs,
288
+ key as unknown as ID<Account>,
289
+ target._schema[ItemsSym]
290
+ );
291
+ }
292
+ })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
293
+ },
294
+ });
295
+
296
+ return entry;
297
+ } else if (key === "perSession") {
298
+ return new Proxy(
299
+ {},
300
+ CoStreamPerSessionProxyHandler(target, receiver)
301
+ );
302
+ } else {
303
+ return Reflect.get(target, key, receiver);
304
+ }
305
+ },
306
+ set(target, key, value, receiver) {
307
+ if (
308
+ key === ItemsSym &&
309
+ typeof value === "object" &&
310
+ SchemaInit in value
311
+ ) {
312
+ (target.constructor as typeof CoStream)._schema ||= {};
313
+ (target.constructor as typeof CoStream)._schema[ItemsSym] =
314
+ value[SchemaInit];
315
+ init(target);
316
+ return true;
317
+ } else {
318
+ return Reflect.set(target, key, value, receiver);
319
+ }
320
+ },
321
+ defineProperty(target, key, descriptor) {
322
+ if (
323
+ descriptor.value &&
324
+ key === ItemsSym &&
325
+ typeof descriptor.value === "object" &&
326
+ SchemaInit in descriptor.value
327
+ ) {
328
+ (target.constructor as typeof CoStream)._schema ||= {};
329
+ (target.constructor as typeof CoStream)._schema[ItemsSym] =
330
+ descriptor.value[SchemaInit];
331
+ init(target);
332
+ return true;
333
+ } else {
334
+ return Reflect.defineProperty(target, key, descriptor);
335
+ }
336
+ },
337
+ ownKeys(target) {
338
+ const keys = Reflect.ownKeys(target);
339
+
340
+ for (const accountID of target._raw.accounts()) {
341
+ keys.push(accountID);
342
+ }
343
+
344
+ return keys;
345
+ },
346
+ getOwnPropertyDescriptor(target, key) {
347
+ if (typeof key === "string" && key.startsWith("co_")) {
348
+ return {
349
+ configurable: true,
350
+ enumerable: true,
351
+ writable: false,
352
+ };
353
+ } else {
354
+ return Reflect.getOwnPropertyDescriptor(target, key);
355
+ }
356
+ },
357
+ };
358
+
359
+ const CoStreamPerSessionProxyHandler = (
360
+ innerTarget: CoStream,
361
+ accessFrom: CoStream
362
+ ): ProxyHandler<Record<string, never>> => ({
363
+ get(_target, key, receiver) {
364
+ if (typeof key === "string" && key.includes("session")) {
365
+ const sessionID = key as SessionID;
366
+ const rawEntry = innerTarget._raw.lastItemIn(sessionID);
367
+
368
+ if (!rawEntry) return;
369
+ const by = cojsonInternals.accountOrAgentIDfromSessionID(sessionID);
370
+
371
+ const entry = entryFromRawEntry(
372
+ accessFrom,
373
+ rawEntry,
374
+ innerTarget._loadedAs,
375
+ cojsonInternals.isAccountID(by)
376
+ ? (by as unknown as ID<Account>)
377
+ : undefined,
378
+ innerTarget._schema[ItemsSym]
379
+ );
380
+
381
+ Object.defineProperty(entry, "all", {
382
+ get: () => {
383
+ const allRawEntries = innerTarget._raw.itemsIn(sessionID);
384
+ return (function* () {
385
+ while (true) {
386
+ const rawEntry = allRawEntries.next();
387
+ if (rawEntry.done) return;
388
+ yield entryFromRawEntry(
389
+ accessFrom,
390
+ rawEntry.value,
391
+ innerTarget._loadedAs,
392
+ cojsonInternals.isAccountID(by)
393
+ ? (by as unknown as ID<Account>)
394
+ : undefined,
395
+ innerTarget._schema[ItemsSym]
396
+ );
397
+ }
398
+ })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
399
+ },
400
+ });
401
+
402
+ return entry;
403
+ } else {
404
+ return Reflect.get(innerTarget, key, receiver);
405
+ }
406
+ },
407
+ ownKeys() {
408
+ return innerTarget._raw.sessions();
409
+ },
410
+ getOwnPropertyDescriptor(target, key) {
411
+ if (typeof key === "string" && key.startsWith("co_")) {
412
+ return {
413
+ configurable: true,
414
+ enumerable: true,
415
+ writable: false,
416
+ };
417
+ } else {
418
+ return Reflect.getOwnPropertyDescriptor(target, key);
419
+ }
420
+ },
421
+ });
422
+
256
423
  export class BinaryCoStream
257
424
  extends CoValueBase
258
425
  implements CoValue<"BinaryCoStream", RawBinaryCoStream>
259
426
  {
260
- id!: ID<this>;
261
- _type!: "BinaryCoStream";
262
- _raw!: RawBinaryCoStream;
427
+ declare id: ID<this>;
428
+ declare _type: "BinaryCoStream";
429
+ declare _raw: RawBinaryCoStream;
263
430
 
264
431
  constructor(
265
- init: [] | undefined,
266
432
  options:
267
433
  | {
268
434
  owner: Account | Group;
@@ -291,6 +457,13 @@ export class BinaryCoStream
291
457
  });
292
458
  }
293
459
 
460
+ static create<S extends BinaryCoStream>(
461
+ this: SubclassedConstructor<S>,
462
+ options: { owner: Account | Group }
463
+ ) {
464
+ return new this(options);
465
+ }
466
+
294
467
  getChunks(options?: {
295
468
  allowUnfinished?: boolean;
296
469
  }):
@@ -1,28 +1,31 @@
1
1
  import {
2
2
  BinaryCoStream,
3
3
  CoMap,
4
- indexSignature,
4
+ co,
5
5
  subscriptionsScopes,
6
6
  } from "../../internal.js";
7
7
 
8
- export class ImageDefinition extends CoMap<ImageDefinition> {
9
- declare originalSize: [number, number];
10
- declare placeholderDataURL?: string;
8
+ export class ImageDefinition extends CoMap {
9
+ originalSize = co.json<[number, number]>();
10
+ placeholderDataURL? = co.string;
11
11
 
12
- [res: `${number}x${number}`]: BinaryCoStream | null;
13
- declare [indexSignature]: BinaryCoStream | null;
12
+ [co.items] = co.ref(BinaryCoStream);
13
+ [res: `${number}x${number}`]: co<BinaryCoStream | null>;
14
14
 
15
- get _highestResAvailable():
16
- | { res: `${number}x${number}`; stream: BinaryCoStream }
17
- | undefined {
15
+ highestResAvailable(options?: {
16
+ maxWidth?: number;
17
+ }): { res: `${number}x${number}`; stream: BinaryCoStream } | undefined {
18
18
  if (!subscriptionsScopes.get(this)) {
19
19
  console.warn(
20
20
  "highestResAvailable() only makes sense when used within a subscription."
21
21
  );
22
22
  }
23
23
 
24
- const resolutions = Object.keys(this).filter((key) =>
25
- key.match(/^\d+x\d+$/)
24
+ const resolutions = Object.keys(this).filter(
25
+ (key) =>
26
+ key.match(/^\d+x\d+$/) &&
27
+ (!options?.maxWidth ||
28
+ Number(key.split("x")[0]) <= options.maxWidth)
26
29
  ) as `${number}x${number}`[];
27
30
 
28
31
  resolutions.sort((a, b) => {
@@ -53,8 +56,3 @@ export class ImageDefinition extends CoMap<ImageDefinition> {
53
56
  );
54
57
  }
55
58
  }
56
- ImageDefinition.encoding({
57
- originalSize: "json",
58
- placeholderDataURL: "json",
59
- [indexSignature]: { ref: () => BinaryCoStream },
60
- });