jazz-tools 0.19.3 → 0.19.5
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/.svelte-kit/__package__/jazz.class.svelte.d.ts +2 -2
- package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
- package/.svelte-kit/__package__/jazz.class.svelte.js +15 -17
- package/.turbo/turbo-build.log +65 -65
- package/CHANGELOG.md +23 -0
- package/dist/{chunk-JPWM4CS2.js → chunk-DFFRRRRF.js} +137 -77
- package/dist/chunk-DFFRRRRF.js.map +1 -0
- package/dist/index.js +14 -7
- package/dist/index.js.map +1 -1
- package/dist/inspector/{custom-element-3JAYHXWQ.js → custom-element-P76EIWEV.js} +301 -142
- package/dist/inspector/{custom-element-3JAYHXWQ.js.map → custom-element-P76EIWEV.js.map} +1 -1
- package/dist/inspector/index.js +281 -122
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts +2 -0
- package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts.map +1 -0
- package/dist/inspector/utils/history.d.ts +5 -1
- package/dist/inspector/utils/history.d.ts.map +1 -1
- package/dist/inspector/viewer/co-plain-text-view.d.ts +4 -2
- package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
- package/dist/inspector/viewer/page.d.ts.map +1 -1
- package/dist/inspector/viewer/use-resolve-covalue.d.ts +0 -1
- package/dist/inspector/viewer/use-resolve-covalue.d.ts.map +1 -1
- package/dist/react-core/hooks.d.ts.map +1 -1
- package/dist/react-core/index.js +4 -17
- package/dist/react-core/index.js.map +1 -1
- package/dist/svelte/jazz.class.svelte.d.ts +2 -2
- package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
- package/dist/svelte/jazz.class.svelte.js +15 -17
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/coFeed.d.ts.map +1 -1
- package/dist/tools/coValues/group.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts +7 -6
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/coValues/request.d.ts.map +1 -1
- package/dist/tools/exports.d.ts +1 -1
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/implementation/refs.d.ts +1 -1
- package/dist/tools/implementation/refs.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +3 -1
- package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
- package/dist/tools/subscribe/SubscriptionScope.d.ts +5 -2
- package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
- package/dist/tools/subscribe/index.d.ts +1 -1
- package/dist/tools/subscribe/index.d.ts.map +1 -1
- package/dist/tools/subscribe/types.d.ts +2 -1
- package/dist/tools/subscribe/types.d.ts.map +1 -1
- package/dist/tools/tests/SubscriptionScope.test.d.ts +2 -0
- package/dist/tools/tests/SubscriptionScope.test.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/inspector/tests/utils/history.test.ts +233 -2
- package/src/inspector/tests/viewer/co-plain-text-view.test.tsx +125 -0
- package/src/inspector/tests/viewer/history-view.test.tsx +134 -2
- package/src/inspector/utils/history.ts +168 -1
- package/src/inspector/viewer/co-plain-text-view.tsx +102 -3
- package/src/inspector/viewer/history-view.tsx +5 -25
- package/src/inspector/viewer/page.tsx +8 -1
- package/src/inspector/viewer/use-resolve-covalue.ts +2 -6
- package/src/react-core/hooks.ts +5 -29
- package/src/svelte/jazz.class.svelte.ts +16 -34
- package/src/tools/coValues/coFeed.ts +10 -7
- package/src/tools/coValues/coMap.ts +10 -7
- package/src/tools/coValues/group.ts +6 -2
- package/src/tools/coValues/interfaces.ts +48 -28
- package/src/tools/coValues/request.ts +12 -8
- package/src/tools/exports.ts +1 -0
- package/src/tools/implementation/refs.ts +9 -17
- package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +62 -30
- package/src/tools/subscribe/SubscriptionScope.ts +38 -2
- package/src/tools/subscribe/index.ts +28 -13
- package/src/tools/subscribe/types.ts +5 -2
- package/src/tools/tests/SubscriptionScope.test.ts +397 -0
- package/src/tools/tests/deepLoading.test.ts +22 -0
- package/src/tools/tests/subscribe.test.ts +69 -0
- package/dist/chunk-JPWM4CS2.js.map +0 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
CoValue,
|
|
3
|
+
CoValueClass,
|
|
4
|
+
MaybeLoaded,
|
|
5
|
+
RefEncoded,
|
|
6
|
+
} from "../internal.js";
|
|
2
7
|
import { createUnloadedCoValue } from "../internal.js";
|
|
3
8
|
import { SubscriptionScope } from "./SubscriptionScope.js";
|
|
4
9
|
import { CoValueLoadingState } from "./types.js";
|
|
@@ -56,16 +61,22 @@ export function accessChildByKey<D extends CoValue>(
|
|
|
56
61
|
);
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
// TODO: this doesn't check the subscription tree loading state
|
|
65
|
+
// so if one of the children is loading, it will return the loading state
|
|
66
|
+
// instead of the latest loaded state
|
|
59
67
|
const value = subscriptionScope.childValues.get(childId);
|
|
60
68
|
|
|
61
69
|
if (value?.type === CoValueLoadingState.LOADED) {
|
|
62
70
|
return value.value;
|
|
63
|
-
} else {
|
|
64
|
-
return createUnloadedCoValue(
|
|
65
|
-
childId,
|
|
66
|
-
value?.type ?? CoValueLoadingState.LOADING,
|
|
67
|
-
);
|
|
68
71
|
}
|
|
72
|
+
|
|
73
|
+
const childNode = subscriptionScope.childNodes.get(childId);
|
|
74
|
+
|
|
75
|
+
if (!childNode) {
|
|
76
|
+
return createUnloadedCoValue(childId, CoValueLoadingState.UNAVAILABLE);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return childNode.getCurrentValue();
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
/**
|
|
@@ -77,9 +88,9 @@ export function accessChildByKey<D extends CoValue>(
|
|
|
77
88
|
* Used for refs that never change (e.g. CoFeed entries, CoMap edits)
|
|
78
89
|
*/
|
|
79
90
|
export function accessChildById<D extends CoValue>(
|
|
80
|
-
parent:
|
|
91
|
+
parent: CoValue,
|
|
81
92
|
childId: string,
|
|
82
|
-
schema: RefEncoded<
|
|
93
|
+
schema: RefEncoded<D>,
|
|
83
94
|
) {
|
|
84
95
|
const subscriptionScope = getSubscriptionScope(parent);
|
|
85
96
|
|
|
@@ -87,12 +98,16 @@ export function accessChildById<D extends CoValue>(
|
|
|
87
98
|
|
|
88
99
|
const value = subscriptionScope.childValues.get(childId);
|
|
89
100
|
|
|
101
|
+
// TODO: this doesn't check the subscription tree loading state
|
|
90
102
|
if (value?.type === CoValueLoadingState.LOADED) {
|
|
91
103
|
return value.value;
|
|
92
|
-
} else {
|
|
93
|
-
return createUnloadedCoValue(
|
|
94
|
-
childId,
|
|
95
|
-
value?.type ?? CoValueLoadingState.LOADING,
|
|
96
|
-
);
|
|
97
104
|
}
|
|
105
|
+
|
|
106
|
+
const childNode = subscriptionScope.childNodes.get(childId);
|
|
107
|
+
|
|
108
|
+
if (!childNode) {
|
|
109
|
+
return createUnloadedCoValue<D>(childId, CoValueLoadingState.UNAVAILABLE);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return childNode.getCurrentValue() as MaybeLoaded<D>;
|
|
98
113
|
}
|
|
@@ -29,10 +29,13 @@ export const CoValueLoadingState = {
|
|
|
29
29
|
export type CoValueLoadingState =
|
|
30
30
|
(typeof CoValueLoadingState)[keyof typeof CoValueLoadingState];
|
|
31
31
|
|
|
32
|
+
export type CoValueErrorState =
|
|
33
|
+
| typeof CoValueLoadingState.UNAVAILABLE
|
|
34
|
+
| typeof CoValueLoadingState.UNAUTHORIZED;
|
|
35
|
+
|
|
32
36
|
export type NotLoadedCoValueState =
|
|
33
37
|
| typeof CoValueLoadingState.LOADING
|
|
34
|
-
|
|
|
35
|
-
| typeof CoValueLoadingState.UNAVAILABLE;
|
|
38
|
+
| CoValueErrorState;
|
|
36
39
|
|
|
37
40
|
export type SubscriptionValue<D extends CoValue, R extends RefsToResolve<D>> =
|
|
38
41
|
| {
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { Account, Group, co, z } from "../exports.js";
|
|
3
|
+
import {
|
|
4
|
+
CoValueLoadingState,
|
|
5
|
+
coValueClassFromCoValueClassOrSchema,
|
|
6
|
+
} from "../internal.js";
|
|
7
|
+
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
|
8
|
+
import { JazzError } from "../subscribe/JazzError.js";
|
|
9
|
+
import { SubscriptionScope } from "../subscribe/SubscriptionScope.js";
|
|
10
|
+
|
|
11
|
+
describe("SubscriptionScope", () => {
|
|
12
|
+
const Person = co.map({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
await setupJazzTestSync();
|
|
18
|
+
|
|
19
|
+
await createJazzTestAccount({
|
|
20
|
+
isCurrentActiveAccount: true,
|
|
21
|
+
creationProps: { name: "Hermes Puggington" },
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("getCurrentValue reference stability", () => {
|
|
26
|
+
it("returns the same reference when called multiple times with the same loading state", () => {
|
|
27
|
+
const person = Person.create({ name: "John" });
|
|
28
|
+
const node = person.$jazz.raw.core.node;
|
|
29
|
+
const id = person.$jazz.id;
|
|
30
|
+
const schema = {
|
|
31
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
32
|
+
optional: false,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
36
|
+
|
|
37
|
+
// Simulate LOADING state
|
|
38
|
+
scope.value = { type: CoValueLoadingState.LOADING, id };
|
|
39
|
+
|
|
40
|
+
const firstCall = scope.getCurrentValue();
|
|
41
|
+
const secondCall = scope.getCurrentValue();
|
|
42
|
+
const thirdCall = scope.getCurrentValue();
|
|
43
|
+
|
|
44
|
+
// All calls should return the same reference
|
|
45
|
+
expect(firstCall).toBe(secondCall);
|
|
46
|
+
expect(secondCall).toBe(thirdCall);
|
|
47
|
+
expect(firstCall).toBe(thirdCall);
|
|
48
|
+
|
|
49
|
+
// Verify it's a NotLoaded value with LOADING state
|
|
50
|
+
expect(firstCall.$jazz.loadingState).toBe(CoValueLoadingState.LOADING);
|
|
51
|
+
expect(firstCall.$isLoaded).toBe(false);
|
|
52
|
+
expect(firstCall.$jazz.id).toBe(id);
|
|
53
|
+
|
|
54
|
+
// Verify the cached value matches
|
|
55
|
+
expect(scope.unloadedValue).toBe(firstCall);
|
|
56
|
+
|
|
57
|
+
scope.destroy();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns different references when the loading state changes", () => {
|
|
61
|
+
const person = Person.create({ name: "John" });
|
|
62
|
+
const node = person.$jazz.raw.core.node;
|
|
63
|
+
const id = person.$jazz.id;
|
|
64
|
+
const schema = {
|
|
65
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
66
|
+
optional: false,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
70
|
+
|
|
71
|
+
// Start with LOADING state
|
|
72
|
+
scope.value = { type: CoValueLoadingState.LOADING, id };
|
|
73
|
+
const loadingValue = scope.getCurrentValue();
|
|
74
|
+
|
|
75
|
+
// Switch to UNAVAILABLE state
|
|
76
|
+
scope.updateValue(
|
|
77
|
+
new JazzError(id, CoValueLoadingState.UNAVAILABLE, [
|
|
78
|
+
{
|
|
79
|
+
code: CoValueLoadingState.UNAVAILABLE,
|
|
80
|
+
message: "The value is unavailable",
|
|
81
|
+
params: { id },
|
|
82
|
+
path: [],
|
|
83
|
+
},
|
|
84
|
+
]),
|
|
85
|
+
);
|
|
86
|
+
const unavailableValue = scope.getCurrentValue();
|
|
87
|
+
|
|
88
|
+
// Switch to UNAUTHORIZED state
|
|
89
|
+
scope.updateValue(
|
|
90
|
+
new JazzError(id, CoValueLoadingState.UNAUTHORIZED, [
|
|
91
|
+
{
|
|
92
|
+
code: CoValueLoadingState.UNAUTHORIZED,
|
|
93
|
+
message: "The current user is not authorized to access this value",
|
|
94
|
+
params: { id },
|
|
95
|
+
path: [],
|
|
96
|
+
},
|
|
97
|
+
]),
|
|
98
|
+
);
|
|
99
|
+
const unauthorizedValue = scope.getCurrentValue();
|
|
100
|
+
|
|
101
|
+
// All should be different references
|
|
102
|
+
expect(loadingValue).not.toBe(unavailableValue);
|
|
103
|
+
expect(loadingValue).not.toBe(unauthorizedValue);
|
|
104
|
+
expect(unavailableValue).not.toBe(unauthorizedValue);
|
|
105
|
+
|
|
106
|
+
// Verify each has the correct loading state
|
|
107
|
+
expect(loadingValue.$jazz.loadingState).toBe(CoValueLoadingState.LOADING);
|
|
108
|
+
expect(unavailableValue.$jazz.loadingState).toBe(
|
|
109
|
+
CoValueLoadingState.UNAVAILABLE,
|
|
110
|
+
);
|
|
111
|
+
expect(unauthorizedValue.$jazz.loadingState).toBe(
|
|
112
|
+
CoValueLoadingState.UNAUTHORIZED,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Verify the cached value is the last one
|
|
116
|
+
expect(scope.unloadedValue).toBe(unauthorizedValue);
|
|
117
|
+
|
|
118
|
+
scope.destroy();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("maintains reference stability across multiple state transitions", () => {
|
|
122
|
+
const person = Person.create({ name: "John" });
|
|
123
|
+
const node = person.$jazz.raw.core.node;
|
|
124
|
+
const id = person.$jazz.id;
|
|
125
|
+
const schema = {
|
|
126
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
127
|
+
optional: false,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
131
|
+
|
|
132
|
+
// Get LOADING value multiple times
|
|
133
|
+
scope.value = { type: CoValueLoadingState.LOADING, id };
|
|
134
|
+
const loading1 = scope.getCurrentValue();
|
|
135
|
+
const loading2 = scope.getCurrentValue();
|
|
136
|
+
expect(loading1).toBe(loading2);
|
|
137
|
+
|
|
138
|
+
// Switch to UNAVAILABLE
|
|
139
|
+
scope.updateValue(
|
|
140
|
+
new JazzError(id, CoValueLoadingState.UNAVAILABLE, [
|
|
141
|
+
{
|
|
142
|
+
code: CoValueLoadingState.UNAVAILABLE,
|
|
143
|
+
message: "The value is unavailable",
|
|
144
|
+
params: { id },
|
|
145
|
+
path: [],
|
|
146
|
+
},
|
|
147
|
+
]),
|
|
148
|
+
);
|
|
149
|
+
const unavailable1 = scope.getCurrentValue();
|
|
150
|
+
expect(unavailable1).not.toBe(loading1);
|
|
151
|
+
|
|
152
|
+
// Get UNAVAILABLE again - should return same reference
|
|
153
|
+
const unavailable2 = scope.getCurrentValue();
|
|
154
|
+
expect(unavailable1).toBe(unavailable2);
|
|
155
|
+
|
|
156
|
+
// Switch to UNAUTHORIZED
|
|
157
|
+
scope.updateValue(
|
|
158
|
+
new JazzError(id, CoValueLoadingState.UNAUTHORIZED, [
|
|
159
|
+
{
|
|
160
|
+
code: CoValueLoadingState.UNAUTHORIZED,
|
|
161
|
+
message: "The current user is not authorized to access this value",
|
|
162
|
+
params: { id },
|
|
163
|
+
path: [],
|
|
164
|
+
},
|
|
165
|
+
]),
|
|
166
|
+
);
|
|
167
|
+
const unauthorized1 = scope.getCurrentValue();
|
|
168
|
+
expect(unauthorized1).not.toBe(unavailable1);
|
|
169
|
+
|
|
170
|
+
// Get UNAUTHORIZED again - should return same reference
|
|
171
|
+
const unauthorized2 = scope.getCurrentValue();
|
|
172
|
+
expect(unauthorized1).toBe(unauthorized2);
|
|
173
|
+
|
|
174
|
+
// Switch back to UNAVAILABLE - should create new reference
|
|
175
|
+
scope.updateValue(
|
|
176
|
+
new JazzError(id, CoValueLoadingState.UNAVAILABLE, [
|
|
177
|
+
{
|
|
178
|
+
code: CoValueLoadingState.UNAVAILABLE,
|
|
179
|
+
message: "The value is unavailable",
|
|
180
|
+
params: { id },
|
|
181
|
+
path: [],
|
|
182
|
+
},
|
|
183
|
+
]),
|
|
184
|
+
);
|
|
185
|
+
const unavailable3 = scope.getCurrentValue();
|
|
186
|
+
expect(unavailable3).not.toBe(unavailable1);
|
|
187
|
+
expect(unavailable3).not.toBe(unavailable2);
|
|
188
|
+
|
|
189
|
+
// Get UNAVAILABLE again - should return same reference as unavailable3
|
|
190
|
+
const unavailable4 = scope.getCurrentValue();
|
|
191
|
+
expect(unavailable3).toBe(unavailable4);
|
|
192
|
+
|
|
193
|
+
scope.destroy();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("returns stable reference when switching back to a previously used state after cache was overwritten", () => {
|
|
197
|
+
const person = Person.create({ name: "John" });
|
|
198
|
+
const node = person.$jazz.raw.core.node;
|
|
199
|
+
const id = person.$jazz.id;
|
|
200
|
+
const schema = {
|
|
201
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
202
|
+
optional: false,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
206
|
+
|
|
207
|
+
// First, get a LOADING value
|
|
208
|
+
scope.value = { type: CoValueLoadingState.LOADING, id };
|
|
209
|
+
const firstLoadingValue = scope.getCurrentValue();
|
|
210
|
+
|
|
211
|
+
// Switch to UNAVAILABLE (this overwrites the cache)
|
|
212
|
+
scope.updateValue(
|
|
213
|
+
new JazzError(id, CoValueLoadingState.UNAVAILABLE, [
|
|
214
|
+
{
|
|
215
|
+
code: CoValueLoadingState.UNAVAILABLE,
|
|
216
|
+
message: "The value is unavailable",
|
|
217
|
+
params: { id },
|
|
218
|
+
path: [],
|
|
219
|
+
},
|
|
220
|
+
]),
|
|
221
|
+
);
|
|
222
|
+
const unavailableValue = scope.getCurrentValue();
|
|
223
|
+
|
|
224
|
+
// Switch back to LOADING (should create a new reference since cache was overwritten)
|
|
225
|
+
scope.value = { type: CoValueLoadingState.LOADING, id };
|
|
226
|
+
const secondLoadingValue = scope.getCurrentValue();
|
|
227
|
+
|
|
228
|
+
// The second LOADING value should be different from the first
|
|
229
|
+
// because the cache was overwritten with UNAVAILABLE
|
|
230
|
+
expect(firstLoadingValue).not.toBe(secondLoadingValue);
|
|
231
|
+
|
|
232
|
+
// But both should have the same loading state
|
|
233
|
+
expect(firstLoadingValue.$jazz.loadingState).toBe(
|
|
234
|
+
CoValueLoadingState.LOADING,
|
|
235
|
+
);
|
|
236
|
+
expect(secondLoadingValue.$jazz.loadingState).toBe(
|
|
237
|
+
CoValueLoadingState.LOADING,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// The cache should now point to the second LOADING value
|
|
241
|
+
expect(scope.unloadedValue).toBe(secondLoadingValue);
|
|
242
|
+
|
|
243
|
+
scope.destroy();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("preserves correct loading state in returned value", () => {
|
|
247
|
+
const person = Person.create({ name: "John" });
|
|
248
|
+
const node = person.$jazz.raw.core.node;
|
|
249
|
+
const id = person.$jazz.id;
|
|
250
|
+
const schema = {
|
|
251
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
252
|
+
optional: false,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
256
|
+
|
|
257
|
+
// Test LOADING state
|
|
258
|
+
scope.value = { type: CoValueLoadingState.LOADING, id };
|
|
259
|
+
const loadingValue = scope.getCurrentValue();
|
|
260
|
+
expect(loadingValue.$jazz.loadingState).toBe(CoValueLoadingState.LOADING);
|
|
261
|
+
expect(loadingValue.$isLoaded).toBe(false);
|
|
262
|
+
expect(loadingValue.$jazz.id).toBe(id);
|
|
263
|
+
|
|
264
|
+
// Test UNAVAILABLE state
|
|
265
|
+
scope.updateValue(
|
|
266
|
+
new JazzError(id, CoValueLoadingState.UNAVAILABLE, [
|
|
267
|
+
{
|
|
268
|
+
code: CoValueLoadingState.UNAVAILABLE,
|
|
269
|
+
message: "The value is unavailable",
|
|
270
|
+
params: { id },
|
|
271
|
+
path: [],
|
|
272
|
+
},
|
|
273
|
+
]),
|
|
274
|
+
);
|
|
275
|
+
const unavailableValue = scope.getCurrentValue();
|
|
276
|
+
expect(unavailableValue.$jazz.loadingState).toBe(
|
|
277
|
+
CoValueLoadingState.UNAVAILABLE,
|
|
278
|
+
);
|
|
279
|
+
expect(unavailableValue.$isLoaded).toBe(false);
|
|
280
|
+
expect(unavailableValue.$jazz.id).toBe(id);
|
|
281
|
+
|
|
282
|
+
// Test UNAUTHORIZED state
|
|
283
|
+
scope.updateValue(
|
|
284
|
+
new JazzError(id, CoValueLoadingState.UNAUTHORIZED, [
|
|
285
|
+
{
|
|
286
|
+
code: CoValueLoadingState.UNAUTHORIZED,
|
|
287
|
+
message: "The current user is not authorized to access this value",
|
|
288
|
+
params: { id },
|
|
289
|
+
path: [],
|
|
290
|
+
},
|
|
291
|
+
]),
|
|
292
|
+
);
|
|
293
|
+
const unauthorizedValue = scope.getCurrentValue();
|
|
294
|
+
expect(unauthorizedValue.$jazz.loadingState).toBe(
|
|
295
|
+
CoValueLoadingState.UNAUTHORIZED,
|
|
296
|
+
);
|
|
297
|
+
expect(unauthorizedValue.$isLoaded).toBe(false);
|
|
298
|
+
expect(unauthorizedValue.$jazz.id).toBe(id);
|
|
299
|
+
|
|
300
|
+
scope.destroy();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("returns LOADING state when shouldSendUpdates returns false", () => {
|
|
304
|
+
const person = Person.create({ name: "John" });
|
|
305
|
+
const node = person.$jazz.raw.core.node;
|
|
306
|
+
const id = person.$jazz.id;
|
|
307
|
+
const schema = {
|
|
308
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
309
|
+
optional: false,
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
313
|
+
|
|
314
|
+
// Set up a loaded value with pending children
|
|
315
|
+
const loadedPerson = Person.create({ name: "Jane" });
|
|
316
|
+
scope.updateValue({
|
|
317
|
+
type: CoValueLoadingState.LOADED,
|
|
318
|
+
value: loadedPerson,
|
|
319
|
+
id: loadedPerson.$jazz.id,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Add a pending child to make shouldSendUpdates return false
|
|
323
|
+
scope.pendingLoadedChildren.add("some-child-id");
|
|
324
|
+
|
|
325
|
+
const value1 = scope.getCurrentValue();
|
|
326
|
+
const value2 = scope.getCurrentValue();
|
|
327
|
+
|
|
328
|
+
// Should return the same LOADING reference
|
|
329
|
+
expect(value1).toBe(value2);
|
|
330
|
+
expect(value1.$jazz.loadingState).toBe(CoValueLoadingState.LOADING);
|
|
331
|
+
expect(value1.$isLoaded).toBe(false);
|
|
332
|
+
|
|
333
|
+
// Clear pending children
|
|
334
|
+
scope.pendingLoadedChildren.clear();
|
|
335
|
+
|
|
336
|
+
// Now should return the loaded value
|
|
337
|
+
const loadedValue = scope.getCurrentValue();
|
|
338
|
+
expect(loadedValue).toBe(loadedPerson);
|
|
339
|
+
expect(loadedValue.$isLoaded).toBe(true);
|
|
340
|
+
|
|
341
|
+
scope.destroy();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("returns error state from errorFromChildren when present", () => {
|
|
345
|
+
const person = Person.create({ name: "John" });
|
|
346
|
+
const node = person.$jazz.raw.core.node;
|
|
347
|
+
const id = person.$jazz.id;
|
|
348
|
+
const schema = {
|
|
349
|
+
ref: coValueClassFromCoValueClassOrSchema(Person),
|
|
350
|
+
optional: false,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const scope = new SubscriptionScope(node, true, id, schema);
|
|
354
|
+
|
|
355
|
+
// Set up a loaded value
|
|
356
|
+
const loadedPerson = Person.create({ name: "Jane" });
|
|
357
|
+
scope.updateValue({
|
|
358
|
+
type: CoValueLoadingState.LOADED,
|
|
359
|
+
value: loadedPerson,
|
|
360
|
+
id: loadedPerson.$jazz.id,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Set up an error from children
|
|
364
|
+
const childError = new JazzError(
|
|
365
|
+
"child-id" as any,
|
|
366
|
+
CoValueLoadingState.UNAVAILABLE,
|
|
367
|
+
[
|
|
368
|
+
{
|
|
369
|
+
code: CoValueLoadingState.UNAVAILABLE,
|
|
370
|
+
message: "Child value is unavailable",
|
|
371
|
+
params: { id: "child-id" },
|
|
372
|
+
path: [],
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
);
|
|
376
|
+
scope.errorFromChildren = childError;
|
|
377
|
+
|
|
378
|
+
const value1 = scope.getCurrentValue();
|
|
379
|
+
const value2 = scope.getCurrentValue();
|
|
380
|
+
|
|
381
|
+
// Should return the same UNAVAILABLE reference
|
|
382
|
+
expect(value1).toBe(value2);
|
|
383
|
+
expect(value1.$jazz.loadingState).toBe(CoValueLoadingState.UNAVAILABLE);
|
|
384
|
+
expect(value1.$isLoaded).toBe(false);
|
|
385
|
+
|
|
386
|
+
// Clear the error
|
|
387
|
+
scope.errorFromChildren = undefined;
|
|
388
|
+
|
|
389
|
+
// Now should return the loaded value
|
|
390
|
+
const loadedValue = scope.getCurrentValue();
|
|
391
|
+
expect(loadedValue).toBe(loadedPerson);
|
|
392
|
+
expect(loadedValue.$isLoaded).toBe(true);
|
|
393
|
+
|
|
394
|
+
scope.destroy();
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
});
|
|
@@ -1169,6 +1169,28 @@ test("should not throw when calling ensureLoaded a record with a deleted ref", a
|
|
|
1169
1169
|
unsub();
|
|
1170
1170
|
});
|
|
1171
1171
|
|
|
1172
|
+
test("should not throw when calling ensureLoaded a record with a non-existent key if there's a catch block", async () => {
|
|
1173
|
+
const Person = co.record(
|
|
1174
|
+
z.string(),
|
|
1175
|
+
co.map({
|
|
1176
|
+
name: z.string(),
|
|
1177
|
+
breed: z.string(),
|
|
1178
|
+
}),
|
|
1179
|
+
);
|
|
1180
|
+
|
|
1181
|
+
const person = Person.create({});
|
|
1182
|
+
|
|
1183
|
+
const loadedPerson = await person.$jazz.ensureLoaded({
|
|
1184
|
+
resolve: {
|
|
1185
|
+
["pet1"]: {
|
|
1186
|
+
$onError: "catch",
|
|
1187
|
+
},
|
|
1188
|
+
},
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
expect(loadedPerson.pet1).toBeUndefined();
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1172
1194
|
// This was a regression that ocurred when we migrated `DeeplyLoaded` to use explicit loading states.
|
|
1173
1195
|
// Keeping this test to prevent it from happening again.
|
|
1174
1196
|
test("deep loaded CoList nested inside another CoValue can be iterated over", async () => {
|
|
@@ -1296,6 +1296,75 @@ describe("subscribeToCoValue", () => {
|
|
|
1296
1296
|
expect(result.data.length).toBe(chunks + 1);
|
|
1297
1297
|
expect(result.data[chunks]).toBe("new entry");
|
|
1298
1298
|
});
|
|
1299
|
+
|
|
1300
|
+
it.fails(
|
|
1301
|
+
"should return the latest loaded state when a deeply loaded child becomes not accessible",
|
|
1302
|
+
async () => {
|
|
1303
|
+
const Dog = co.map({
|
|
1304
|
+
name: z.string(),
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
const Person = co.map({
|
|
1308
|
+
name: z.string(),
|
|
1309
|
+
dog: Dog,
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
const reader = await createJazzTestAccount({
|
|
1313
|
+
isCurrentActiveAccount: true,
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
const creator = await createJazzTestAccount({
|
|
1317
|
+
isCurrentActiveAccount: true,
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
const dogGroup = Group.create(creator);
|
|
1321
|
+
dogGroup.addMember(reader, "reader");
|
|
1322
|
+
|
|
1323
|
+
const everyone = Group.create(creator);
|
|
1324
|
+
everyone.addMember("everyone", "reader");
|
|
1325
|
+
|
|
1326
|
+
const dog = Dog.create({ name: "Giggino" }, dogGroup);
|
|
1327
|
+
const person = Person.create({ name: "Guido", dog }, everyone);
|
|
1328
|
+
|
|
1329
|
+
let result = null as Loaded<typeof Person, { dog: true }> | null;
|
|
1330
|
+
|
|
1331
|
+
const updateFn = vi.fn().mockImplementation((value) => {
|
|
1332
|
+
result = value;
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
const unsubscribe = subscribeToCoValue(
|
|
1336
|
+
coValueClassFromCoValueClassOrSchema(Person),
|
|
1337
|
+
person.$jazz.id,
|
|
1338
|
+
{
|
|
1339
|
+
loadAs: reader,
|
|
1340
|
+
resolve: {
|
|
1341
|
+
dog: true,
|
|
1342
|
+
},
|
|
1343
|
+
},
|
|
1344
|
+
updateFn,
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
onTestFinished(unsubscribe);
|
|
1348
|
+
|
|
1349
|
+
await waitFor(() => {
|
|
1350
|
+
assert(result);
|
|
1351
|
+
expect(result.name).toBe("Guido");
|
|
1352
|
+
assertLoaded(result.dog);
|
|
1353
|
+
expect(result.dog.name).toBe("Giggino");
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
// Make the dog not accessible by removing the reader from the dog's group
|
|
1357
|
+
dogGroup.removeMember(reader);
|
|
1358
|
+
|
|
1359
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1360
|
+
|
|
1361
|
+
assert(result);
|
|
1362
|
+
|
|
1363
|
+
// The parent & child loading state should be in sync, but because the child loading state
|
|
1364
|
+
// is mutable it becomes not loaded while the parent is still loaded
|
|
1365
|
+
expect(result.$isLoaded).toBe(result.dog.$isLoaded);
|
|
1366
|
+
},
|
|
1367
|
+
);
|
|
1299
1368
|
});
|
|
1300
1369
|
|
|
1301
1370
|
describe("getSubscriptionScope", () => {
|