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

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