jazz-tools 0.7.0-alpha.26 → 0.7.0-alpha.28

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +39 -36
  2. package/CHANGELOG.md +17 -0
  3. package/dist/coValues/account.js +15 -8
  4. package/dist/coValues/account.js.map +1 -1
  5. package/dist/coValues/coList.js +3 -2
  6. package/dist/coValues/coList.js.map +1 -1
  7. package/dist/coValues/coStream.js +38 -19
  8. package/dist/coValues/coStream.js.map +1 -1
  9. package/dist/coValues/group.js +26 -1
  10. package/dist/coValues/group.js.map +1 -1
  11. package/dist/implementation/schema.js +5 -3
  12. package/dist/implementation/schema.js.map +1 -1
  13. package/dist/implementation/symbols.js +5 -0
  14. package/dist/implementation/symbols.js.map +1 -0
  15. package/dist/internal.js +1 -0
  16. package/dist/internal.js.map +1 -1
  17. package/dist/tests/coList.test.js +15 -15
  18. package/dist/tests/coList.test.js.map +1 -1
  19. package/dist/tests/coMap.test.js +15 -16
  20. package/dist/tests/coMap.test.js.map +1 -1
  21. package/dist/tests/coStream.test.js +4 -4
  22. package/dist/tests/coStream.test.js.map +1 -1
  23. package/dist/tests/groupsAndAccounts.test.js +70 -0
  24. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  25. package/package.json +2 -2
  26. package/src/coValues/account.ts +21 -10
  27. package/src/coValues/coList.ts +5 -8
  28. package/src/coValues/coStream.ts +48 -30
  29. package/src/coValues/group.ts +62 -16
  30. package/src/implementation/schema.ts +5 -9
  31. package/src/implementation/symbols.ts +12 -0
  32. package/src/internal.ts +1 -0
  33. package/src/tests/coList.test.ts +15 -15
  34. package/src/tests/coMap.test.ts +24 -19
  35. package/src/tests/coStream.test.ts +4 -4
  36. package/src/tests/groupsAndAccounts.test.ts +77 -0
@@ -275,22 +275,24 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
275
275
  get: () => {
276
276
  const allRawEntries = target._raw.itemsBy(key as AccountID);
277
277
  return (function* () {
278
- const rawEntry = allRawEntries.next();
279
- if (rawEntry.done) return;
280
- yield entryFromRawEntry(
281
- receiver,
282
- rawEntry.value,
283
- target._loadedAs,
284
- key as unknown as ID<Account>,
285
- target._schema[ItemsSym]
286
- );
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
+ }
287
289
  })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
288
290
  },
289
291
  });
290
292
 
291
293
  return entry;
292
294
  } else if (key === "perSession") {
293
- return new Proxy(receiver, CoStreamPerSessionProxyHandler);
295
+ return new Proxy({}, CoStreamPerSessionProxyHandler(target, receiver));
294
296
  } else {
295
297
  return Reflect.get(target, key, receiver);
296
298
  }
@@ -348,50 +350,66 @@ export const CoStreamProxyHandler: ProxyHandler<CoStream> = {
348
350
  },
349
351
  };
350
352
 
351
- const CoStreamPerSessionProxyHandler: ProxyHandler<CoStream> = {
352
- get(target, key, receiver) {
353
+ const CoStreamPerSessionProxyHandler = (innerTarget: CoStream, accessFrom: CoStream): ProxyHandler<Record<string, never>> => ({
354
+ get(_target, key, receiver) {
353
355
  if (typeof key === "string" && key.includes("session")) {
354
356
  const sessionID = key as SessionID;
355
- const rawEntry = target._raw.lastItemIn(sessionID);
357
+ const rawEntry = innerTarget._raw.lastItemIn(sessionID);
356
358
 
357
359
  if (!rawEntry) return;
358
360
  const by = cojsonInternals.accountOrAgentIDfromSessionID(sessionID);
359
361
 
360
362
  const entry = entryFromRawEntry(
361
- target,
363
+ accessFrom,
362
364
  rawEntry,
363
- target._loadedAs,
365
+ innerTarget._loadedAs,
364
366
  cojsonInternals.isAccountID(by)
365
367
  ? (by as unknown as ID<Account>)
366
368
  : undefined,
367
- target._schema[ItemsSym]
369
+ innerTarget._schema[ItemsSym]
368
370
  );
369
371
 
370
372
  Object.defineProperty(entry, "all", {
371
373
  get: () => {
372
- const allRawEntries = target._raw.itemsIn(sessionID);
374
+ const allRawEntries = innerTarget._raw.itemsIn(sessionID);
373
375
  return (function* () {
374
- const rawEntry = allRawEntries.next();
375
- if (rawEntry.done) return;
376
- yield entryFromRawEntry(
377
- receiver,
378
- rawEntry.value,
379
- target._loadedAs,
380
- cojsonInternals.isAccountID(by)
381
- ? (by as unknown as ID<Account>)
382
- : undefined,
383
- target._schema[ItemsSym]
384
- );
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
+ }
385
389
  })() satisfies IterableIterator<SingleCoStreamEntry<any>>;
386
390
  },
387
391
  });
388
392
 
389
393
  return entry;
390
394
  } else {
391
- return Reflect.get(target, key, receiver);
395
+ return Reflect.get(innerTarget, key, receiver);
392
396
  }
393
397
  },
394
- };
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
+ });
395
413
 
396
414
  export class BinaryCoStream
397
415
  extends CoValueBase
@@ -1,5 +1,11 @@
1
- import type { Everyone, RawGroup, Role } from "cojson";
2
- import type { CoValue, ID, JsonEncoded, RefEncoded, Schema } from "../internal.js";
1
+ import type { AccountID, Everyone, RawGroup, Role } from "cojson";
2
+ import type {
3
+ CoValue,
4
+ ID,
5
+ JsonEncoded,
6
+ RefEncoded,
7
+ Schema,
8
+ } from "../internal.js";
3
9
  import {
4
10
  Account,
5
11
  CoMap,
@@ -8,16 +14,32 @@ import {
8
14
  co,
9
15
  isControlledAccount,
10
16
  AccountAndGroupProxyHandler,
17
+ MembersSym,
11
18
  } from "../internal.js";
12
19
 
13
20
  export class Profile extends CoMap<{ name: co<string> }> {
14
21
  name = co.string;
15
22
  }
16
23
 
24
+ type GroupSchema<Def extends Group> = {
25
+ profile: NonNullable<Def["profile"]> extends CoValue
26
+ ? RefEncoded<NonNullable<Def["profile"]>>
27
+ : JsonEncoded;
28
+ root: NonNullable<Def["root"]> extends CoValue
29
+ ? RefEncoded<NonNullable<Def["root"]>>
30
+ : JsonEncoded;
31
+ [MembersSym]: RefEncoded<NonNullable<Def[MembersSym]>>;
32
+ };
33
+
17
34
  export class Group<
18
- Def extends { profile: Profile | null; root: CoMap | null } = {
35
+ Def extends {
19
36
  profile: Profile | null;
20
37
  root: CoMap | null;
38
+ [MembersSym]: Account | null;
39
+ } = {
40
+ profile: Profile | null;
41
+ root: CoMap | null;
42
+ [MembersSym]: Account | null;
21
43
  },
22
44
  >
23
45
  extends CoValueBase
@@ -31,31 +53,23 @@ export class Group<
31
53
  declare _raw: RawGroup;
32
54
 
33
55
  static _schema: any;
34
- get _schema(): {
35
- profile: Def["profile"] extends CoValue
36
- ? RefEncoded<Def["profile"]>
37
- : JsonEncoded;
38
- root: Def["root"] extends CoValue
39
- ? RefEncoded<Def["root"]>
40
- : JsonEncoded;
41
- } {
56
+ get _schema(): GroupSchema<this> {
42
57
  return (this.constructor as typeof Group)._schema;
43
58
  }
44
59
  static {
45
60
  this._schema = {
46
61
  profile: "json" satisfies Schema,
47
62
  root: "json" satisfies Schema,
63
+ [MembersSym]: () => Account satisfies Schema,
48
64
  } as any;
49
65
  Object.defineProperty(this.prototype, "_schema", {
50
66
  get: () => this._schema,
51
67
  });
52
68
  }
53
69
 
54
- profile!: Def["profile"] extends Profile
55
- ? Def["profile"] | null
56
- : undefined;
57
-
58
- root!: Def["root"] extends CoMap ? Def["root"] | null : undefined;
70
+ declare profile: Def["profile"] | null;
71
+ declare root: Def["root"] | null;
72
+ declare [MembersSym]: Def[MembersSym] | null;
59
73
 
60
74
  get _refs(): {
61
75
  profile: Def["profile"] extends Profile ? Ref<Def["profile"]> : never;
@@ -136,4 +150,36 @@ export class Group<
136
150
  addMember(member: Everyone | Account, role: Role) {
137
151
  this._raw.addMember(member === "everyone" ? member : member._raw, role);
138
152
  }
153
+
154
+ get members() {
155
+ return this._raw
156
+ .keys()
157
+ .filter((key) => {
158
+ return key === "everyone" || key.startsWith("co_");
159
+ })
160
+ .map((id) => {
161
+ const role = this._raw.get(id as Everyone | AccountID);
162
+ const accountID =
163
+ id === "everyone"
164
+ ? undefined
165
+ : (id as unknown as ID<Account>);
166
+ const ref =
167
+ accountID &&
168
+ new Ref<NonNullable<this[MembersSym]>>(
169
+ accountID,
170
+ this._loadedAs,
171
+ this._schema[MembersSym]
172
+ );
173
+ const accessRef = () => ref?.accessFrom(this);
174
+
175
+ return {
176
+ id: id as unknown as Everyone | ID<this[MembersSym]>,
177
+ role,
178
+ ref,
179
+ get account() {
180
+ return accessRef();
181
+ },
182
+ };
183
+ });
184
+ }
139
185
  }
@@ -10,15 +10,6 @@ export type IfCo<C, R> = C extends infer _A | infer B
10
10
  : never
11
11
  : never;
12
12
 
13
- export const SchemaInit = Symbol.for("SchemaInit");
14
- export type SchemaInit = typeof SchemaInit;
15
-
16
- export const InitValues = Symbol.for("InitValues");
17
- export type InitValues = typeof InitValues;
18
-
19
- export const ItemsSym = Symbol.for("items");
20
- export type ItemsSym = typeof ItemsSym;
21
-
22
13
  export const co = {
23
14
  string: {
24
15
  [SchemaInit]: "json" satisfies Schema,
@@ -29,6 +20,9 @@ export const co = {
29
20
  boolean: {
30
21
  [SchemaInit]: "json" satisfies Schema,
31
22
  } as unknown as co<boolean>,
23
+ null: {
24
+ [SchemaInit]: "json" satisfies Schema,
25
+ } as unknown as co<null>,
32
26
  literal: <T extends string | number | boolean>(_lit: T): co<T> => {
33
27
  return { [SchemaInit]: "json" satisfies Schema } as any;
34
28
  },
@@ -44,6 +38,7 @@ export const co = {
44
38
  return { [SchemaInit]: arg satisfies Schema } as any;
45
39
  },
46
40
  items: ItemsSym as ItemsSym,
41
+ members: MembersSym as MembersSym,
47
42
  };
48
43
 
49
44
  export type JsonEncoded = "json";
@@ -89,6 +84,7 @@ export type EffectSchemaWithInputAndOutput<A, I = A> = EffectSchema<
89
84
  export type Encoder<V> = EffectSchemaWithInputAndOutput<V, JsonValue>;
90
85
 
91
86
  import { Date } from "@effect/schema/Schema";
87
+ import { SchemaInit, ItemsSym, MembersSym } from "./symbols.js";
92
88
 
93
89
  export const Encoders = {
94
90
  Date,
@@ -0,0 +1,12 @@
1
+
2
+ export const SchemaInit = Symbol.for("SchemaInit");
3
+ export type SchemaInit = typeof SchemaInit;
4
+
5
+ export const InitValues = Symbol.for("InitValues");
6
+ export type InitValues = typeof InitValues;
7
+
8
+ export const ItemsSym = Symbol.for("items");
9
+ export type ItemsSym = typeof ItemsSym;
10
+
11
+ export const MembersSym = Symbol.for("members");
12
+ export type MembersSym = typeof MembersSym;
package/src/internal.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./implementation/symbols.js";
1
2
  export * from './implementation/inspect.js';
2
3
  export * from "./coValues/interfaces.js";
3
4
 
@@ -17,7 +17,7 @@ beforeEach(async () => {
17
17
 
18
18
  describe("Simple CoList operations", async () => {
19
19
  const me = await Account.create({
20
- name: "Hermes Puggington",
20
+ creationProps: { name: "Hermes Puggington" },
21
21
  });
22
22
 
23
23
  class TestList extends CoList.Of(co.string) {}
@@ -97,19 +97,19 @@ describe("Simple CoList operations", async () => {
97
97
  expect(list._raw.asArray()).toEqual(["butter", "onion"]);
98
98
  });
99
99
 
100
- // test("splice", () => {
101
- // const list = new TestList(["bread", "butter", "onion"], {
102
- // owner: me,
103
- // });
104
- // list.splice(1, 1, "salt", "pepper");
105
- // expect(list.length).toBe(4);
106
- // expect(list._raw.asArray()).toEqual([
107
- // "bread",
108
- // "salt",
109
- // "pepper",
110
- // "onion",
111
- // ]);
112
- // });
100
+ test("splice", () => {
101
+ const list = new TestList(["bread", "butter", "onion"], {
102
+ owner: me,
103
+ });
104
+ list.splice(1, 1, "salt", "pepper");
105
+ expect(list.length).toBe(4);
106
+ expect(list._raw.asArray()).toEqual([
107
+ "bread",
108
+ "salt",
109
+ "pepper",
110
+ "onion",
111
+ ]);
112
+ });
113
113
  });
114
114
  });
115
115
 
@@ -126,7 +126,7 @@ describe("CoList resolution", async () => {
126
126
 
127
127
  const initNodeAndList = async () => {
128
128
  const me = await Account.create({
129
- name: "Hermes Puggington",
129
+ creationProps: { name: "Hermes Puggington" },
130
130
  });
131
131
 
132
132
  const list = new TestList(
@@ -17,7 +17,7 @@ beforeEach(async () => {
17
17
 
18
18
  describe("Simple CoMap operations", async () => {
19
19
  const me = await Account.create({
20
- name: "Hermes Puggington",
20
+ creationProps: { name: "Hermes Puggington" },
21
21
  });
22
22
 
23
23
  class TestMap extends CoMap<TestMap> {
@@ -50,11 +50,7 @@ describe("Simple CoMap operations", async () => {
50
50
  expect(map._height).toEqual(10);
51
51
  expect(map.birthday).toEqual(birthday);
52
52
  expect(map._raw.get("birthday")).toEqual(birthday.toISOString());
53
- expect(Object.keys(map)).toEqual([
54
- "color",
55
- "_height",
56
- "birthday",
57
- ]);
53
+ expect(Object.keys(map)).toEqual(["color", "_height", "birthday"]);
58
54
  });
59
55
 
60
56
  describe("Mutation", () => {
@@ -202,7 +198,7 @@ describe("CoMap resolution", async () => {
202
198
 
203
199
  const initNodeAndMap = async () => {
204
200
  const me = await Account.create({
205
- name: "Hermes Puggington",
201
+ creationProps: { name: "Hermes Puggington" },
206
202
  });
207
203
 
208
204
  const map = new TestMap(
@@ -394,7 +390,7 @@ describe("CoMap resolution", async () => {
394
390
 
395
391
  test("Construction with optional", async () => {
396
392
  const me = await Account.create({
397
- name: "Hermes Puggington",
393
+ creationProps: { name: "Hermes Puggington" },
398
394
  });
399
395
 
400
396
  const mapWithout = new TestMapWithOptionalRef(
@@ -437,7 +433,7 @@ describe("CoMap resolution", async () => {
437
433
 
438
434
  test("Construction with index signature", async () => {
439
435
  const me = await Account.create({
440
- name: "Hermes Puggington",
436
+ creationProps: { name: "Hermes Puggington" },
441
437
  });
442
438
 
443
439
  const record = new TestRecord(
@@ -454,18 +450,18 @@ describe("CoMap resolution", async () => {
454
450
  expect(record._raw.get("other")).toEqual(3);
455
451
  expect(Object.keys(record)).toEqual(["height", "other"]);
456
452
  expect(record.toJSON()).toMatchObject({
457
- "_type": "CoMap",
458
- "height": 5,
459
- "id": expect.any(String),
460
- "other": 3,
461
- });
453
+ _type: "CoMap",
454
+ height: 5,
455
+ id: expect.any(String),
456
+ other: 3,
457
+ });
462
458
  });
463
459
 
464
460
  class TestRecord2 extends CoMap.Record(co.number) {}
465
461
 
466
462
  test("Construction with index signature (shorthand)", async () => {
467
463
  const me = await Account.create({
468
- name: "Hermes Puggington",
464
+ creationProps: { name: "Hermes Puggington" },
469
465
  });
470
466
 
471
467
  const record = new TestRecord2(
@@ -487,13 +483,19 @@ describe("CoMap resolution", async () => {
487
483
 
488
484
  test("Construction with index signature ref", async () => {
489
485
  const me = await Account.create({
490
- name: "Hermes Puggington",
486
+ creationProps: { name: "Hermes Puggington" },
491
487
  });
492
488
 
493
489
  const record = new TestRecordRef(
494
490
  {
495
- firstNested: new TwiceNestedMap({ taste: "sour" }, { owner: me }),
496
- secondNested: new TwiceNestedMap({ taste: "sweet" }, { owner: me }),
491
+ firstNested: new TwiceNestedMap(
492
+ { taste: "sour" },
493
+ { owner: me }
494
+ ),
495
+ secondNested: new TwiceNestedMap(
496
+ { taste: "sweet" },
497
+ { owner: me }
498
+ ),
497
499
  },
498
500
  { owner: me }
499
501
  );
@@ -503,6 +505,9 @@ describe("CoMap resolution", async () => {
503
505
  expect(record.secondNested?.taste).toEqual("sweet");
504
506
  expect(record.secondNested?.id).toBeDefined();
505
507
  expect(Object.keys(record)).toEqual(["firstNested", "secondNested"]);
506
- expect(Object.keys(record._refs)).toEqual(["firstNested", "secondNested"]);
508
+ expect(Object.keys(record._refs)).toEqual([
509
+ "firstNested",
510
+ "secondNested",
511
+ ]);
507
512
  });
508
513
  });
@@ -18,7 +18,7 @@ beforeEach(async () => {
18
18
 
19
19
  describe("Simple CoStream operations", async () => {
20
20
  const me = await Account.create({
21
- name: "Hermes Puggington",
21
+ creationProps: { name: "Hermes Puggington" },
22
22
  });
23
23
 
24
24
  class TestStream extends CoStream.Of(co.string) {}
@@ -56,7 +56,7 @@ describe("CoStream resolution", async () => {
56
56
 
57
57
  const initNodeAndStream = async () => {
58
58
  const me = await Account.create({
59
- name: "Hermes Puggington",
59
+ creationProps: { name: "Hermes Puggington" },
60
60
  });
61
61
 
62
62
  const stream = new TestStream(
@@ -257,7 +257,7 @@ describe("CoStream resolution", async () => {
257
257
 
258
258
  describe("Simple BinaryCoStream operations", async () => {
259
259
  const me = await Account.create({
260
- name: "Hermes Puggington",
260
+ creationProps: { name: "Hermes Puggington" },
261
261
  });
262
262
 
263
263
  const stream = new BinaryCoStream(undefined, { owner: me });
@@ -285,7 +285,7 @@ describe("Simple BinaryCoStream operations", async () => {
285
285
  describe("BinaryCoStream loading & Subscription", async () => {
286
286
  const initNodeAndStream = async () => {
287
287
  const me = await Account.create({
288
- name: "Hermes Puggington",
288
+ creationProps: { name: "Hermes Puggington" },
289
289
  });
290
290
 
291
291
  const stream = new BinaryCoStream(undefined, { owner: me });
@@ -0,0 +1,77 @@
1
+ import { expect, describe, test, beforeEach } from "vitest";
2
+
3
+ import { webcrypto } from "node:crypto";
4
+ import { Account, jazzReady, CoMap, co, Group } from "..";
5
+
6
+ if (!("crypto" in globalThis)) {
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ (globalThis as any).crypto = webcrypto;
9
+ }
10
+
11
+ beforeEach(async () => {
12
+ await jazzReady;
13
+ });
14
+
15
+ describe("Custom accounts and groups", async () => {
16
+ class CustomProfile extends CoMap<CustomProfile> {
17
+ name = co.string;
18
+ color = co.string;
19
+ }
20
+
21
+ class CustomAccount extends Account<CustomAccount> {
22
+ profile = co.ref(CustomProfile);
23
+ root = co.ref(CoMap);
24
+
25
+ migrate(creationProps?: { name: string }) {
26
+ if (creationProps) {
27
+ const profileGroup = new Group({ owner: this });
28
+ profileGroup.addMember("everyone", "reader");
29
+ this.profile = new CustomProfile(
30
+ { name: creationProps.name, color: "blue" },
31
+ { owner: this }
32
+ );
33
+ }
34
+ }
35
+ }
36
+
37
+ class CustomGroup extends Group<CustomGroup> {
38
+ profile = co.null;
39
+ root = co.null;
40
+ [co.members] = co.ref(CustomAccount);
41
+ }
42
+
43
+ type T = CustomGroup[typeof co.members];
44
+
45
+ test("Custom account and group", async () => {
46
+ const me = await CustomAccount.create({
47
+ creationProps: { name: "Hermes Puggington" },
48
+ });
49
+
50
+ expect(me.profile).toBeDefined();
51
+ expect(me.profile?.name).toBe("Hermes Puggington");
52
+ expect(me.profile?.color).toBe("blue");
53
+
54
+ const group = new CustomGroup({ owner: me });
55
+ group.addMember("everyone", "reader");
56
+
57
+ expect(group.members).toMatchObject([
58
+ { id: me.id, role: "admin" },
59
+ { id: "everyone", role: "reader" },
60
+ ]);
61
+
62
+ await new Promise<void>((resolve) => {
63
+ group.subscribe((update) => {
64
+ const myProfile = update.members.find((member) => {
65
+ return member.id === me.id && member.account?.profile;
66
+ });
67
+ if (myProfile) {
68
+ expect(myProfile.account?.profile?.name).toBe(
69
+ "Hermes Puggington"
70
+ );
71
+ expect(myProfile.account?.profile?.color).toBe("blue");
72
+ resolve();
73
+ }
74
+ });
75
+ });
76
+ });
77
+ });