jazz-tools 0.7.35-unique.2 → 0.8.0
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.
- package/.turbo/turbo-build.log +2 -2
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +44 -57
- package/CHANGELOG.md +27 -7
- package/dist/coValues/account.js +8 -2
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.js +5 -2
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.js +16 -9
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.js +30 -3
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/interfaces.js +5 -2
- package/dist/coValues/interfaces.js.map +1 -1
- package/dist/implementation/createContext.js +55 -5
- package/dist/implementation/createContext.js.map +1 -1
- package/dist/implementation/devtoolsFormatters.js +1 -0
- package/dist/implementation/devtoolsFormatters.js.map +1 -1
- package/dist/implementation/refs.js +8 -2
- package/dist/implementation/refs.js.map +1 -1
- package/dist/implementation/schema.js.map +1 -1
- package/dist/implementation/subscriptionScope.js +4 -3
- package/dist/implementation/subscriptionScope.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tests/coMap.test.js +92 -1
- package/dist/tests/coMap.test.js.map +1 -1
- package/dist/tests/coStream.test.js +37 -0
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/deepLoading.test.js +7 -7
- package/dist/tests/deepLoading.test.js.map +1 -1
- package/package.json +4 -4
- package/src/coValues/account.ts +9 -3
- package/src/coValues/coList.ts +5 -1
- package/src/coValues/coMap.ts +56 -17
- package/src/coValues/coStream.ts +38 -4
- package/src/coValues/interfaces.ts +8 -4
- package/src/implementation/createContext.ts +117 -11
- package/src/implementation/devtoolsFormatters.ts +1 -0
- package/src/implementation/refs.ts +13 -6
- package/src/implementation/schema.ts +1 -0
- package/src/implementation/subscriptionScope.ts +28 -25
- package/src/index.ts +3 -0
- package/src/tests/coMap.test.ts +139 -1
- package/src/tests/coStream.test.ts +53 -0
- package/src/tests/deepLoading.test.ts +7 -7
@@ -5,6 +5,7 @@ import type {
|
|
5
5
|
ID,
|
6
6
|
CoValueClass,
|
7
7
|
CoValueFromRaw,
|
8
|
+
AnonymousJazzAgent,
|
8
9
|
} from "../internal.js";
|
9
10
|
|
10
11
|
export const subscriptionsScopes = new WeakMap<
|
@@ -17,7 +18,7 @@ const TRACE_INVALIDATIONS = false;
|
|
17
18
|
|
18
19
|
export class SubscriptionScope<Root extends CoValue> {
|
19
20
|
scopeID: string = `scope-${Math.random().toString(36).slice(2)}`;
|
20
|
-
subscriber: Account;
|
21
|
+
subscriber: Account | AnonymousJazzAgent;
|
21
22
|
entries = new Map<
|
22
23
|
ID<CoValue>,
|
23
24
|
| { state: "loading"; immediatelyUnsub?: boolean }
|
@@ -89,32 +90,34 @@ export class SubscriptionScope<Root extends CoValue> {
|
|
89
90
|
immediatelyUnsub: false,
|
90
91
|
} as const;
|
91
92
|
this.entries.set(accessedOrSetId, loadingEntry);
|
92
|
-
|
93
|
-
.
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
93
|
+
const node =
|
94
|
+
this.subscriber._type === "Account"
|
95
|
+
? this.subscriber._raw.core.node
|
96
|
+
: this.subscriber.node;
|
97
|
+
void node.loadCoValueCore(accessedOrSetId).then((core) => {
|
98
|
+
if (
|
99
|
+
loadingEntry.state === "loading" &&
|
100
|
+
loadingEntry.immediatelyUnsub
|
101
|
+
) {
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
if (core !== "unavailable") {
|
105
|
+
const entry = {
|
106
|
+
state: "loaded" as const,
|
107
|
+
rawUnsub: () => {}, // placeholder
|
108
|
+
};
|
109
|
+
this.entries.set(accessedOrSetId, entry);
|
107
110
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
const rawUnsub = core.subscribe((rawUpdate) => {
|
112
|
+
// console.log("ref update", this.scopeID, accessedOrSetId, JSON.stringify(rawUpdate))
|
113
|
+
if (!rawUpdate) return;
|
114
|
+
this.invalidate(accessedOrSetId);
|
115
|
+
this.scheduleUpdate();
|
116
|
+
});
|
114
117
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
+
entry.rawUnsub = rawUnsub;
|
119
|
+
}
|
120
|
+
});
|
118
121
|
}
|
119
122
|
}
|
120
123
|
|
package/src/index.ts
CHANGED
package/src/tests/coMap.test.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { expect, describe, test } from "vitest";
|
1
|
+
import { expect, describe, test, expectTypeOf } from "vitest";
|
2
2
|
import { connectedPeers } from "cojson/src/streamUtils.js";
|
3
3
|
import {
|
4
4
|
Account,
|
@@ -592,6 +592,7 @@ describe("CoMap applyDiff", async () => {
|
|
592
592
|
birthday = co.encoded(Encoders.Date);
|
593
593
|
nested = co.ref(NestedMap);
|
594
594
|
optionalField = co.optional.string;
|
595
|
+
optionalNested = co.optional.ref(NestedMap);
|
595
596
|
}
|
596
597
|
|
597
598
|
class NestedMap extends CoMap {
|
@@ -737,6 +738,143 @@ describe("CoMap applyDiff", async () => {
|
|
737
738
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
738
739
|
expect((map as any).invalidField).toBeUndefined();
|
739
740
|
});
|
741
|
+
|
742
|
+
test("applyDiff with optional reference set to null", () => {
|
743
|
+
const map = TestMap.create(
|
744
|
+
{
|
745
|
+
name: "Jack",
|
746
|
+
age: 50,
|
747
|
+
isActive: true,
|
748
|
+
birthday: new Date("1970-01-01"),
|
749
|
+
nested: NestedMap.create({ value: "original" }, { owner: me }),
|
750
|
+
optionalNested: NestedMap.create(
|
751
|
+
{ value: "optional" },
|
752
|
+
{ owner: me },
|
753
|
+
),
|
754
|
+
},
|
755
|
+
{ owner: me },
|
756
|
+
);
|
757
|
+
|
758
|
+
const newValues = {
|
759
|
+
optionalNested: null,
|
760
|
+
};
|
761
|
+
|
762
|
+
map.applyDiff(newValues);
|
763
|
+
|
764
|
+
expect(map.optionalNested).toBeNull();
|
765
|
+
});
|
766
|
+
|
767
|
+
test("applyDiff with required reference set to null should throw", () => {
|
768
|
+
const map = TestMap.create(
|
769
|
+
{
|
770
|
+
name: "Kate",
|
771
|
+
age: 55,
|
772
|
+
isActive: true,
|
773
|
+
birthday: new Date("1965-01-01"),
|
774
|
+
nested: NestedMap.create({ value: "original" }, { owner: me }),
|
775
|
+
},
|
776
|
+
{ owner: me },
|
777
|
+
);
|
778
|
+
|
779
|
+
const newValues = {
|
780
|
+
nested: null,
|
781
|
+
};
|
782
|
+
|
783
|
+
// @ts-expect-error testing invalid usage
|
784
|
+
expect(() => map.applyDiff(newValues)).toThrowError(
|
785
|
+
"Cannot set required reference nested to null",
|
786
|
+
);
|
787
|
+
});
|
788
|
+
});
|
789
|
+
|
790
|
+
describe("CoMap Typescript validation", async () => {
|
791
|
+
const me = await Account.create({
|
792
|
+
creationProps: { name: "Hermes Puggington" },
|
793
|
+
crypto: Crypto,
|
794
|
+
});
|
795
|
+
|
796
|
+
test("Is not ok to pass null into a required ref", () => {
|
797
|
+
class TestMap extends CoMap {
|
798
|
+
required = co.ref(NestedMap);
|
799
|
+
optional = co.optional.ref(NestedMap);
|
800
|
+
}
|
801
|
+
|
802
|
+
class NestedMap extends CoMap {
|
803
|
+
value = co.string;
|
804
|
+
}
|
805
|
+
|
806
|
+
expectTypeOf<typeof TestMap.create<TestMap>>().toBeCallableWith(
|
807
|
+
{
|
808
|
+
optional: NestedMap.create({ value: "" }, { owner: me }),
|
809
|
+
// @ts-expect-error null can't be passed to a non-optional field
|
810
|
+
required: null,
|
811
|
+
},
|
812
|
+
{ owner: me },
|
813
|
+
);
|
814
|
+
});
|
815
|
+
|
816
|
+
test("Is not ok if a required ref is omitted", () => {
|
817
|
+
class TestMap extends CoMap {
|
818
|
+
required = co.ref(NestedMap);
|
819
|
+
optional = co.ref(NestedMap, { optional: true });
|
820
|
+
}
|
821
|
+
|
822
|
+
class NestedMap extends CoMap {
|
823
|
+
value = co.string;
|
824
|
+
}
|
825
|
+
|
826
|
+
expectTypeOf<typeof TestMap.create<TestMap>>().toBeCallableWith(
|
827
|
+
// @ts-expect-error non-optional fields can't be omitted
|
828
|
+
{},
|
829
|
+
{ owner: me },
|
830
|
+
);
|
831
|
+
});
|
832
|
+
|
833
|
+
test("Is ok to omit optional fields", () => {
|
834
|
+
class TestMap extends CoMap {
|
835
|
+
required = co.ref(NestedMap);
|
836
|
+
optional = co.ref(NestedMap, { optional: true });
|
837
|
+
}
|
838
|
+
|
839
|
+
class NestedMap extends CoMap {
|
840
|
+
value = co.string;
|
841
|
+
}
|
842
|
+
|
843
|
+
expectTypeOf<typeof TestMap.create<TestMap>>().toBeCallableWith(
|
844
|
+
{
|
845
|
+
required: NestedMap.create({ value: "" }, { owner: me }),
|
846
|
+
},
|
847
|
+
{ owner: me },
|
848
|
+
);
|
849
|
+
|
850
|
+
expectTypeOf<typeof TestMap.create<TestMap>>().toBeCallableWith(
|
851
|
+
{
|
852
|
+
required: NestedMap.create({ value: "" }, { owner: me }),
|
853
|
+
optional: null,
|
854
|
+
},
|
855
|
+
{ owner: me },
|
856
|
+
);
|
857
|
+
});
|
858
|
+
|
859
|
+
test("the required refs should be nullable", () => {
|
860
|
+
class TestMap extends CoMap {
|
861
|
+
required = co.ref(NestedMap);
|
862
|
+
optional = co.ref(NestedMap, { optional: true });
|
863
|
+
}
|
864
|
+
|
865
|
+
class NestedMap extends CoMap {
|
866
|
+
value = co.string;
|
867
|
+
}
|
868
|
+
|
869
|
+
const map = TestMap.create(
|
870
|
+
{
|
871
|
+
required: NestedMap.create({ value: "" }, { owner: me }),
|
872
|
+
},
|
873
|
+
{ owner: me },
|
874
|
+
);
|
875
|
+
|
876
|
+
expectTypeOf(map.required).toBeNullable();
|
877
|
+
});
|
740
878
|
});
|
741
879
|
|
742
880
|
describe("Creating and finding unique CoMaps", async () => {
|
@@ -443,3 +443,56 @@ describe("BinaryCoStream loading & Subscription", async () => {
|
|
443
443
|
});
|
444
444
|
});
|
445
445
|
});
|
446
|
+
|
447
|
+
describe("BinaryCoStream.loadAsBlob", async () => {
|
448
|
+
async function setup() {
|
449
|
+
const me = await Account.create({
|
450
|
+
creationProps: { name: "Hermes Puggington" },
|
451
|
+
crypto: Crypto,
|
452
|
+
});
|
453
|
+
|
454
|
+
const stream = BinaryCoStream.create({ owner: me });
|
455
|
+
|
456
|
+
stream.start({ mimeType: "text/plain" });
|
457
|
+
|
458
|
+
return { stream, me };
|
459
|
+
}
|
460
|
+
|
461
|
+
test("resolves only when the stream is ended", async () => {
|
462
|
+
const { stream, me } = await setup();
|
463
|
+
stream.push(new Uint8Array([1]));
|
464
|
+
|
465
|
+
const promise = BinaryCoStream.loadAsBlob(stream.id, me);
|
466
|
+
|
467
|
+
await stream.ensureLoaded([]);
|
468
|
+
|
469
|
+
stream.push(new Uint8Array([2]));
|
470
|
+
stream.end();
|
471
|
+
|
472
|
+
const blob = await promise;
|
473
|
+
|
474
|
+
// The promise resolves only when the stream is ended
|
475
|
+
// so we get a blob with all the chunks
|
476
|
+
expect(blob?.size).toBe(2);
|
477
|
+
});
|
478
|
+
|
479
|
+
test("resolves with an unfinshed blob if allowUnfinished: true", async () => {
|
480
|
+
const { stream, me } = await setup();
|
481
|
+
stream.push(new Uint8Array([1]));
|
482
|
+
|
483
|
+
const promise = BinaryCoStream.loadAsBlob(stream.id, me, {
|
484
|
+
allowUnfinished: true,
|
485
|
+
});
|
486
|
+
|
487
|
+
await stream.ensureLoaded([]);
|
488
|
+
|
489
|
+
stream.push(new Uint8Array([2]));
|
490
|
+
stream.end();
|
491
|
+
|
492
|
+
const blob = await promise;
|
493
|
+
|
494
|
+
// The promise resolves before the stream is ended
|
495
|
+
// so we get a blob only with the first chunk
|
496
|
+
expect(blob?.size).toBe(1);
|
497
|
+
});
|
498
|
+
});
|
@@ -60,6 +60,7 @@ describe("Deep loading with depth arg", async () => {
|
|
60
60
|
});
|
61
61
|
|
62
62
|
test("loading a deeply nested object will wait until all required refs are loaded", async () => {
|
63
|
+
const ownership = { owner: me };
|
63
64
|
const map = TestMap.create(
|
64
65
|
{
|
65
66
|
list: TestList.create(
|
@@ -70,19 +71,19 @@ describe("Deep loading with depth arg", async () => {
|
|
70
71
|
[
|
71
72
|
InnermostMap.create(
|
72
73
|
{ value: "hello" },
|
73
|
-
|
74
|
+
ownership,
|
74
75
|
),
|
75
76
|
],
|
76
|
-
|
77
|
+
ownership,
|
77
78
|
),
|
78
79
|
},
|
79
|
-
|
80
|
+
ownership,
|
80
81
|
),
|
81
82
|
],
|
82
|
-
|
83
|
+
ownership,
|
83
84
|
),
|
84
85
|
},
|
85
|
-
|
86
|
+
ownership,
|
86
87
|
);
|
87
88
|
|
88
89
|
const map1 = await TestMap.load(map.id, meOnSecondPeer, {});
|
@@ -141,8 +142,7 @@ describe("Deep loading with depth arg", async () => {
|
|
141
142
|
throw new Error("map4 is undefined");
|
142
143
|
}
|
143
144
|
expect(map4.list[0]?.stream).not.toBe(null);
|
144
|
-
|
145
|
-
// expect(map4.list[0]?.stream?.[me.id]).toBe(undefined)
|
145
|
+
expect(map4.list[0]?.stream?.[me.id]).not.toBe(null);
|
146
146
|
expect(map4.list[0]?.stream?.byMe?.value).toBe(null);
|
147
147
|
|
148
148
|
const map5 = await TestMap.load(map.id, meOnSecondPeer, {
|