jazz-tools 0.18.6 → 0.18.8
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 +55 -55
- package/CHANGELOG.md +28 -0
- package/dist/better-auth/auth/server.d.ts.map +1 -1
- package/dist/better-auth/auth/server.js +8 -4
- package/dist/better-auth/auth/server.js.map +1 -1
- package/dist/{chunk-45VKEOXG.js → chunk-QF3R3C4N.js} +75 -22
- package/dist/chunk-QF3R3C4N.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/inspector/{custom-element-IBHKHN27.js → custom-element-G6SPZEBR.js} +292 -31
- package/dist/inspector/custom-element-G6SPZEBR.js.map +1 -0
- package/dist/inspector/index.d.ts +1 -1
- package/dist/inspector/index.js +302 -41
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/ui/button.d.ts +1 -1
- package/dist/inspector/ui/button.d.ts.map +1 -1
- package/dist/inspector/ui/heading.d.ts +2 -1
- package/dist/inspector/ui/heading.d.ts.map +1 -1
- package/dist/inspector/ui/input.d.ts.map +1 -1
- package/dist/inspector/ui/modal.d.ts +16 -0
- package/dist/inspector/ui/modal.d.ts.map +1 -0
- package/dist/inspector/viewer/delete-local-data.d.ts +2 -0
- package/dist/inspector/viewer/delete-local-data.d.ts.map +1 -0
- package/dist/inspector/viewer/{inpsector-button.d.ts → inspector-button.d.ts} +1 -1
- package/dist/inspector/viewer/{inpsector-button.d.ts.map → inspector-button.d.ts.map} +1 -1
- package/dist/inspector/viewer/new-app.d.ts +1 -1
- package/dist/inspector/viewer/new-app.d.ts.map +1 -1
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react-core/hooks.d.ts +133 -0
- package/dist/react-core/hooks.d.ts.map +1 -1
- package/dist/react-core/index.js +85 -17
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/tests/useCoStateWithSelector.test.d.ts +2 -0
- package/dist/react-core/tests/useCoStateWithSelector.test.d.ts.map +1 -0
- package/dist/react-native-core/hooks.d.ts +1 -1
- package/dist/react-native-core/hooks.d.ts.map +1 -1
- package/dist/react-native-core/index.js +3 -1
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/CoFieldInit.d.ts +5 -5
- package/dist/tools/coValues/CoFieldInit.d.ts.map +1 -1
- package/dist/tools/coValues/CoValueBase.d.ts +14 -0
- package/dist/tools/coValues/CoValueBase.d.ts.map +1 -1
- package/dist/tools/coValues/coMap.d.ts +0 -12
- package/dist/tools/coValues/coMap.d.ts.map +1 -1
- package/dist/tools/implementation/createContext.d.ts +2 -1
- package/dist/tools/implementation/createContext.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +5 -3
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
- package/dist/tools/tests/utils.d.ts.map +1 -1
- package/dist/worker/index.d.ts +4 -0
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +4 -2
- package/dist/worker/index.js.map +1 -1
- package/package.json +6 -4
- package/src/better-auth/auth/server.ts +8 -4
- package/src/better-auth/auth/tests/server.test.ts +2 -2
- package/src/inspector/index.tsx +1 -1
- package/src/inspector/ui/button.tsx +15 -1
- package/src/inspector/ui/heading.tsx +7 -2
- package/src/inspector/ui/input.tsx +6 -2
- package/src/inspector/ui/modal.tsx +158 -0
- package/src/inspector/viewer/delete-local-data.tsx +101 -0
- package/src/inspector/viewer/new-app.tsx +3 -1
- package/src/react/hooks.tsx +1 -0
- package/src/react/index.ts +1 -0
- package/src/react-core/hooks.ts +162 -0
- package/src/react-core/tests/useCoStateWithSelector.test.ts +149 -0
- package/src/react-native-core/hooks.tsx +1 -0
- package/src/tools/coValues/CoFieldInit.ts +5 -5
- package/src/tools/coValues/CoValueBase.ts +32 -0
- package/src/tools/coValues/coList.ts +35 -0
- package/src/tools/coValues/coMap.ts +0 -18
- package/src/tools/implementation/createContext.ts +9 -2
- package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +22 -8
- package/src/tools/tests/coList.test.ts +41 -0
- package/src/tools/tests/coMap.test.ts +37 -0
- package/src/tools/tests/coPlainText.test.ts +24 -0
- package/src/tools/tests/createContext.test.ts +24 -0
- package/src/tools/tests/deepLoading.test.ts +2 -0
- package/src/tools/tests/patterns/requestToJoin.test.ts +14 -6
- package/src/tools/tests/utils.ts +1 -0
- package/src/worker/index.ts +6 -0
- package/dist/chunk-45VKEOXG.js.map +0 -1
- package/dist/inspector/custom-element-IBHKHN27.js.map +0 -1
- /package/src/inspector/viewer/{inpsector-button.tsx → inspector-button.tsx} +0 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
// @vitest-environment happy-dom
|
2
|
+
|
3
|
+
import { cojsonInternals } from "cojson";
|
4
|
+
import { Account, co, Loaded, z } from "jazz-tools";
|
5
|
+
import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
|
6
|
+
import { useCoStateWithSelector } from "../index.js";
|
7
|
+
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
8
|
+
import { renderHook, waitFor } from "./testUtils.js";
|
9
|
+
import { useRef } from "react";
|
10
|
+
|
11
|
+
beforeEach(async () => {
|
12
|
+
await setupJazzTestSync();
|
13
|
+
|
14
|
+
await createJazzTestAccount({
|
15
|
+
isCurrentActiveAccount: true,
|
16
|
+
});
|
17
|
+
});
|
18
|
+
|
19
|
+
cojsonInternals.setCoValueLoadingRetryDelay(300);
|
20
|
+
|
21
|
+
const useRenderCount = <T>(hook: () => T) => {
|
22
|
+
const renderCountRef = useRef(0);
|
23
|
+
const result = hook();
|
24
|
+
renderCountRef.current = renderCountRef.current + 1;
|
25
|
+
return {
|
26
|
+
renderCount: renderCountRef.current,
|
27
|
+
result,
|
28
|
+
};
|
29
|
+
};
|
30
|
+
|
31
|
+
describe("useCoStateWithSelector", () => {
|
32
|
+
it("should not re-render when a nested coValue is updated and not selected", async () => {
|
33
|
+
const TestMap = co.map({
|
34
|
+
value: z.string(),
|
35
|
+
get nested() {
|
36
|
+
return TestMap.optional();
|
37
|
+
},
|
38
|
+
});
|
39
|
+
|
40
|
+
const map = TestMap.create({
|
41
|
+
value: "1",
|
42
|
+
nested: TestMap.create({
|
43
|
+
value: "1",
|
44
|
+
}),
|
45
|
+
});
|
46
|
+
|
47
|
+
const { result } = renderHook(() =>
|
48
|
+
useRenderCount(() =>
|
49
|
+
useCoStateWithSelector(TestMap, map.$jazz.id, {
|
50
|
+
resolve: {
|
51
|
+
nested: true,
|
52
|
+
},
|
53
|
+
select: (v) => v?.value,
|
54
|
+
}),
|
55
|
+
),
|
56
|
+
);
|
57
|
+
|
58
|
+
await waitFor(() => {
|
59
|
+
expect(result.current.result).not.toBeUndefined();
|
60
|
+
});
|
61
|
+
|
62
|
+
for (let i = 0; i < 100; i++) {
|
63
|
+
map.nested!.$jazz.set("value", `${i}`);
|
64
|
+
await Account.getMe().$jazz.waitForAllCoValuesSync();
|
65
|
+
}
|
66
|
+
|
67
|
+
expect(result.current.result).toEqual("1");
|
68
|
+
expect(result.current.renderCount).toEqual(1);
|
69
|
+
});
|
70
|
+
|
71
|
+
it("should re-render when a nested coValue is updated and selected", async () => {
|
72
|
+
const TestMap = co.map({
|
73
|
+
value: z.string(),
|
74
|
+
get nested() {
|
75
|
+
return TestMap.optional();
|
76
|
+
},
|
77
|
+
});
|
78
|
+
|
79
|
+
const map = TestMap.create({
|
80
|
+
value: "1",
|
81
|
+
nested: TestMap.create({
|
82
|
+
value: "1",
|
83
|
+
}),
|
84
|
+
});
|
85
|
+
|
86
|
+
const { result } = renderHook(() =>
|
87
|
+
useRenderCount(() =>
|
88
|
+
useCoStateWithSelector(TestMap, map.$jazz.id, {
|
89
|
+
resolve: {
|
90
|
+
nested: true,
|
91
|
+
},
|
92
|
+
select: (v) => v?.nested?.value,
|
93
|
+
}),
|
94
|
+
),
|
95
|
+
);
|
96
|
+
|
97
|
+
await waitFor(() => {
|
98
|
+
expect(result.current.result).not.toBeUndefined();
|
99
|
+
});
|
100
|
+
|
101
|
+
for (let i = 1; i <= 100; i++) {
|
102
|
+
map.nested!.$jazz.set("value", `${i}`);
|
103
|
+
await Account.getMe().$jazz.waitForAllCoValuesSync();
|
104
|
+
}
|
105
|
+
|
106
|
+
expect(result.current.result).toEqual("100");
|
107
|
+
|
108
|
+
// skips re-render on i = 1, only re-renders on i = [2,100], so initial render + 99 renders = 100
|
109
|
+
expect(result.current.renderCount).toEqual(100);
|
110
|
+
|
111
|
+
expectTypeOf(result.current.result).toEqualTypeOf<string | undefined>();
|
112
|
+
});
|
113
|
+
|
114
|
+
it("should not re-render when equalityFn always returns true", async () => {
|
115
|
+
const TestMap = co.map({
|
116
|
+
value: z.string(),
|
117
|
+
get nested() {
|
118
|
+
return TestMap.optional();
|
119
|
+
},
|
120
|
+
});
|
121
|
+
|
122
|
+
const map = TestMap.create({
|
123
|
+
value: "1",
|
124
|
+
nested: TestMap.create({
|
125
|
+
value: "1",
|
126
|
+
}),
|
127
|
+
});
|
128
|
+
|
129
|
+
const { result } = renderHook(() =>
|
130
|
+
useRenderCount(() =>
|
131
|
+
useCoStateWithSelector(TestMap, map.$jazz.id, {
|
132
|
+
resolve: {
|
133
|
+
nested: true,
|
134
|
+
},
|
135
|
+
select: (v) => v?.nested?.value,
|
136
|
+
equalityFn: () => true,
|
137
|
+
}),
|
138
|
+
),
|
139
|
+
);
|
140
|
+
|
141
|
+
for (let i = 1; i <= 100; i++) {
|
142
|
+
map.nested!.$jazz.set("value", `${i}`);
|
143
|
+
await Account.getMe().$jazz.waitForAllCoValuesSync();
|
144
|
+
}
|
145
|
+
|
146
|
+
expect(result.current.result).toEqual("1");
|
147
|
+
expect(result.current.renderCount).toEqual(1);
|
148
|
+
});
|
149
|
+
});
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { CoFeed } from "./coFeed";
|
2
|
-
import { CoList } from "./coList";
|
3
|
-
import { CoMap, CoMapInit } from "./coMap";
|
4
|
-
import { CoPlainText } from "./coPlainText";
|
5
|
-
import { CoRichText } from "./coRichText";
|
1
|
+
import { CoFeed } from "./coFeed.js";
|
2
|
+
import { CoList } from "./coList.js";
|
3
|
+
import { CoMap, CoMapInit } from "./coMap.js";
|
4
|
+
import { CoPlainText } from "./coPlainText.js";
|
5
|
+
import { CoRichText } from "./coRichText.js";
|
6
6
|
|
7
7
|
/**
|
8
8
|
* Returns the type of values that can be used to initialize a field of the provided type.
|
@@ -77,4 +77,36 @@ export abstract class CoValueJazzApi<V extends CoValue> {
|
|
77
77
|
|
78
78
|
return new AnonymousJazzAgent(this.localNode);
|
79
79
|
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* The timestamp of the creation time of the CoValue
|
83
|
+
*
|
84
|
+
* @category Content
|
85
|
+
*/
|
86
|
+
get createdAt(): number {
|
87
|
+
const createdAt = this.raw.core.verified.header.meta?.createdAt;
|
88
|
+
|
89
|
+
if (typeof createdAt === "string") {
|
90
|
+
return new Date(createdAt).getTime();
|
91
|
+
}
|
92
|
+
|
93
|
+
return this.raw.core.earliestTxMadeAt;
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* The timestamp of the last updated time of the CoValue
|
98
|
+
*
|
99
|
+
* Returns the creation time if there are no updates.
|
100
|
+
*
|
101
|
+
* @category Content
|
102
|
+
*/
|
103
|
+
get lastUpdatedAt(): number {
|
104
|
+
const value = this.raw.core.latestTxMadeAt;
|
105
|
+
|
106
|
+
if (value === 0) {
|
107
|
+
return this.createdAt;
|
108
|
+
}
|
109
|
+
|
110
|
+
return value;
|
111
|
+
}
|
80
112
|
}
|
@@ -971,4 +971,39 @@ const CoListProxyHandler: ProxyHandler<CoList> = {
|
|
971
971
|
return Reflect.has(target, key);
|
972
972
|
}
|
973
973
|
},
|
974
|
+
ownKeys(target) {
|
975
|
+
const keys = Reflect.ownKeys(target);
|
976
|
+
// Add numeric indices for all entries in the list
|
977
|
+
const indexKeys = target.$jazz.raw.entries().map((_entry, i) => String(i));
|
978
|
+
keys.push(...indexKeys);
|
979
|
+
return keys;
|
980
|
+
},
|
981
|
+
getOwnPropertyDescriptor(target, key) {
|
982
|
+
if (key === TypeSym) {
|
983
|
+
// Make TypeSym non-enumerable so it doesn't show up in Object.keys()
|
984
|
+
return {
|
985
|
+
enumerable: false,
|
986
|
+
configurable: true,
|
987
|
+
writable: false,
|
988
|
+
value: target[TypeSym],
|
989
|
+
};
|
990
|
+
} else if (key in target) {
|
991
|
+
return Reflect.getOwnPropertyDescriptor(target, key);
|
992
|
+
} else if (typeof key === "string" && !isNaN(+key)) {
|
993
|
+
const index = Number(key);
|
994
|
+
if (index >= 0 && index < target.$jazz.raw.entries().length) {
|
995
|
+
return {
|
996
|
+
enumerable: true,
|
997
|
+
configurable: true,
|
998
|
+
writable: true,
|
999
|
+
};
|
1000
|
+
}
|
1001
|
+
} else if (key === "length") {
|
1002
|
+
return {
|
1003
|
+
enumerable: false,
|
1004
|
+
configurable: false,
|
1005
|
+
writable: false,
|
1006
|
+
};
|
1007
|
+
}
|
1008
|
+
},
|
974
1009
|
};
|
@@ -836,24 +836,6 @@ class CoMapJazzApi<M extends CoMap> extends CoValueJazzApi<M> {
|
|
836
836
|
return this.getRaw();
|
837
837
|
}
|
838
838
|
|
839
|
-
/**
|
840
|
-
* The timestamp of the creation time of the CoMap
|
841
|
-
*
|
842
|
-
* @category Content
|
843
|
-
*/
|
844
|
-
get createdAt(): number {
|
845
|
-
return this.raw.earliestTxMadeAt ?? Number.MAX_SAFE_INTEGER;
|
846
|
-
}
|
847
|
-
|
848
|
-
/**
|
849
|
-
* The timestamp of the last updated time of the CoMap
|
850
|
-
*
|
851
|
-
* @category Content
|
852
|
-
*/
|
853
|
-
get lastUpdatedAt(): number {
|
854
|
-
return this.raw.latestTxMadeAt;
|
855
|
-
}
|
856
|
-
|
857
839
|
/** @internal */
|
858
840
|
get schema(): CoMapFieldSchema {
|
859
841
|
return (this.coMap.constructor as typeof CoMap)._schema;
|
@@ -96,6 +96,7 @@ export async function createJazzContextFromExistingCredentials<
|
|
96
96
|
AccountSchema: PropsAccountSchema,
|
97
97
|
sessionProvider,
|
98
98
|
onLogOut,
|
99
|
+
asActiveAccount,
|
99
100
|
}: {
|
100
101
|
credentials: Credentials;
|
101
102
|
peersToLoadFrom: Peer[];
|
@@ -104,6 +105,7 @@ export async function createJazzContextFromExistingCredentials<
|
|
104
105
|
sessionProvider: SessionProvider;
|
105
106
|
onLogOut?: () => void;
|
106
107
|
storage?: StorageAPI;
|
108
|
+
asActiveAccount: boolean;
|
107
109
|
}): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
|
108
110
|
const { sessionID, sessionDone } = await sessionProvider(
|
109
111
|
credentials.accountID,
|
@@ -125,14 +127,18 @@ export async function createJazzContextFromExistingCredentials<
|
|
125
127
|
storage,
|
126
128
|
migration: async (rawAccount, _node, creationProps) => {
|
127
129
|
const account = AccountClass.fromRaw(rawAccount) as InstanceOfSchema<S>;
|
128
|
-
|
130
|
+
if (asActiveAccount) {
|
131
|
+
activeAccountContext.set(account);
|
132
|
+
}
|
129
133
|
|
130
134
|
await account.applyMigration(creationProps);
|
131
135
|
},
|
132
136
|
});
|
133
137
|
|
134
138
|
const account = AccountClass.fromNode(node);
|
135
|
-
|
139
|
+
if (asActiveAccount) {
|
140
|
+
activeAccountContext.set(account);
|
141
|
+
}
|
136
142
|
|
137
143
|
return {
|
138
144
|
node,
|
@@ -245,6 +251,7 @@ export async function createJazzContext<
|
|
245
251
|
authSecretStorage.clearWithoutNotify();
|
246
252
|
},
|
247
253
|
storage: options.storage,
|
254
|
+
asActiveAccount: true,
|
248
255
|
});
|
249
256
|
} else {
|
250
257
|
const secretSeed = options.crypto.newRandomSecretSeed();
|
@@ -180,7 +180,9 @@ export interface CoMapSchema<
|
|
180
180
|
*
|
181
181
|
* @returns A new CoMap schema with all fields optional.
|
182
182
|
*/
|
183
|
-
partial
|
183
|
+
partial<Keys extends keyof Shape = keyof Shape>(
|
184
|
+
keys?: { [key in Keys]: true },
|
185
|
+
): CoMapSchema<PartialShape<Shape, Keys>, CatchAll, Owner>;
|
184
186
|
}
|
185
187
|
|
186
188
|
export function createCoreCoMapSchema<
|
@@ -281,10 +283,17 @@ export function enrichCoMapSchema<
|
|
281
283
|
|
282
284
|
return coMapDefiner(pickedShape);
|
283
285
|
},
|
284
|
-
partial:
|
286
|
+
partial: <Keys extends keyof Shape = keyof Shape>(
|
287
|
+
keys?: { [key in Keys]: true },
|
288
|
+
) => {
|
285
289
|
const partialShape: Record<string, AnyZodOrCoValueSchema> = {};
|
286
290
|
|
287
291
|
for (const [key, value] of Object.entries(coValueSchema.shape)) {
|
292
|
+
if (keys && !keys[key as Keys]) {
|
293
|
+
partialShape[key] = value;
|
294
|
+
continue;
|
295
|
+
}
|
296
|
+
|
288
297
|
if (isAnyCoValueSchema(value)) {
|
289
298
|
partialShape[key] = coOptionalDefiner(value);
|
290
299
|
} else {
|
@@ -341,10 +350,15 @@ export type CoMapInstanceCoValuesNullable<Shape extends z.core.$ZodLooseShape> =
|
|
341
350
|
>;
|
342
351
|
};
|
343
352
|
|
344
|
-
export type PartialShape<
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
353
|
+
export type PartialShape<
|
354
|
+
Shape extends z.core.$ZodLooseShape,
|
355
|
+
PartialKeys extends keyof Shape = keyof Shape,
|
356
|
+
> = Simplify<{
|
357
|
+
-readonly [key in keyof Shape]: key extends PartialKeys
|
358
|
+
? Shape[key] extends AnyZodSchema
|
359
|
+
? z.ZodOptional<Shape[key]>
|
360
|
+
: Shape[key] extends CoreCoValueSchema
|
361
|
+
? CoOptionalSchema<Shape[key]>
|
362
|
+
: never
|
363
|
+
: Shape[key];
|
350
364
|
}>;
|
@@ -122,6 +122,25 @@ describe("Simple CoList operations", async () => {
|
|
122
122
|
expect(list[0]).toEqual("milk");
|
123
123
|
});
|
124
124
|
|
125
|
+
test("CoList keys can be iterated over just like an array's", () => {
|
126
|
+
const TestList = co.list(z.string());
|
127
|
+
const list = ["a", "b", "c", "d", "e"];
|
128
|
+
const coList = TestList.create(list);
|
129
|
+
const keys = [];
|
130
|
+
for (const key in coList) {
|
131
|
+
keys.push(key);
|
132
|
+
}
|
133
|
+
expect(keys).toEqual(Object.keys(list));
|
134
|
+
expect(Object.keys(coList)).toEqual(Object.keys(list));
|
135
|
+
});
|
136
|
+
|
137
|
+
test("a CoList is structurally equal to an array", () => {
|
138
|
+
const TestList = co.list(z.string());
|
139
|
+
const list = ["a", "b", "c", "d", "e"];
|
140
|
+
const coList = TestList.create(list);
|
141
|
+
expect(coList).toEqual(list);
|
142
|
+
});
|
143
|
+
|
125
144
|
describe("Mutation", () => {
|
126
145
|
test("assignment", () => {
|
127
146
|
const list = TestList.create(["bread", "butter", "onion"], {
|
@@ -1314,3 +1333,25 @@ describe("co.list schema", () => {
|
|
1314
1333
|
expect(keywords[1]?.toString()).toEqual("world");
|
1315
1334
|
});
|
1316
1335
|
});
|
1336
|
+
|
1337
|
+
describe("lastUpdatedAt", () => {
|
1338
|
+
test("empty list last updated time", () => {
|
1339
|
+
const emptyList = co.list(z.number()).create([]);
|
1340
|
+
|
1341
|
+
expect(emptyList.$jazz.lastUpdatedAt).not.toEqual(0);
|
1342
|
+
expect(emptyList.$jazz.lastUpdatedAt).toEqual(emptyList.$jazz.createdAt);
|
1343
|
+
});
|
1344
|
+
|
1345
|
+
test("last update should change on push", async () => {
|
1346
|
+
const list = co.list(z.string()).create(["John"]);
|
1347
|
+
|
1348
|
+
expect(list.$jazz.lastUpdatedAt).not.toEqual(0);
|
1349
|
+
|
1350
|
+
const updatedAt = list.$jazz.lastUpdatedAt;
|
1351
|
+
|
1352
|
+
await new Promise((r) => setTimeout(r, 10));
|
1353
|
+
list.$jazz.push("Jane");
|
1354
|
+
|
1355
|
+
expect(list.$jazz.lastUpdatedAt).not.toEqual(updatedAt);
|
1356
|
+
});
|
1357
|
+
});
|
@@ -2646,6 +2646,43 @@ describe("co.map schema", () => {
|
|
2646
2646
|
expect(draftPerson.pet).toEqual(rex);
|
2647
2647
|
});
|
2648
2648
|
|
2649
|
+
test("creates a new CoMap schema by making some properties optional", () => {
|
2650
|
+
const Dog = co.map({
|
2651
|
+
name: z.string(),
|
2652
|
+
breed: z.string(),
|
2653
|
+
});
|
2654
|
+
const Person = co.map({
|
2655
|
+
name: z.string(),
|
2656
|
+
age: z.number(),
|
2657
|
+
pet: Dog,
|
2658
|
+
});
|
2659
|
+
|
2660
|
+
const DraftPerson = Person.partial({
|
2661
|
+
pet: true,
|
2662
|
+
});
|
2663
|
+
|
2664
|
+
const draftPerson = DraftPerson.create({
|
2665
|
+
name: "John",
|
2666
|
+
age: 20,
|
2667
|
+
});
|
2668
|
+
|
2669
|
+
expect(draftPerson.$jazz.has("pet")).toBe(false);
|
2670
|
+
|
2671
|
+
const rex = Dog.create({ name: "Rex", breed: "Labrador" });
|
2672
|
+
draftPerson.$jazz.set("pet", rex);
|
2673
|
+
|
2674
|
+
expect(draftPerson.pet).toEqual(rex);
|
2675
|
+
|
2676
|
+
expect(draftPerson.$jazz.has("pet")).toBe(true);
|
2677
|
+
|
2678
|
+
draftPerson.$jazz.delete("pet");
|
2679
|
+
|
2680
|
+
expect(draftPerson.$jazz.has("pet")).toBe(false);
|
2681
|
+
|
2682
|
+
// @ts-expect-error - should not allow deleting required properties
|
2683
|
+
draftPerson.$jazz.delete("age");
|
2684
|
+
});
|
2685
|
+
|
2649
2686
|
test("the new schema includes catchall properties", () => {
|
2650
2687
|
const Person = co
|
2651
2688
|
.map({
|
@@ -183,6 +183,7 @@ describe("CoPlainText", () => {
|
|
183
183
|
sessionProvider: randomSessionProvider,
|
184
184
|
peersToLoadFrom: [initialAsPeer],
|
185
185
|
crypto: Crypto,
|
186
|
+
asActiveAccount: true,
|
186
187
|
});
|
187
188
|
|
188
189
|
// Load the text on the second peer
|
@@ -214,6 +215,7 @@ describe("CoPlainText", () => {
|
|
214
215
|
sessionProvider: randomSessionProvider,
|
215
216
|
peersToLoadFrom: [initialAsPeer],
|
216
217
|
crypto: Crypto,
|
218
|
+
asActiveAccount: true,
|
217
219
|
});
|
218
220
|
|
219
221
|
const queue = new Channel();
|
@@ -242,3 +244,25 @@ describe("CoPlainText", () => {
|
|
242
244
|
expect(update3.toString()).toBe("hello world");
|
243
245
|
});
|
244
246
|
});
|
247
|
+
|
248
|
+
describe("lastUpdatedAt", () => {
|
249
|
+
test("empty text last updated time", () => {
|
250
|
+
const text = co.plainText().create("");
|
251
|
+
|
252
|
+
expect(text.$jazz.lastUpdatedAt).toEqual(text.$jazz.createdAt);
|
253
|
+
expect(text.$jazz.lastUpdatedAt).not.toEqual(0);
|
254
|
+
});
|
255
|
+
|
256
|
+
test("last update should change on push", async () => {
|
257
|
+
const text = co.plainText().create("John");
|
258
|
+
|
259
|
+
expect(text.$jazz.lastUpdatedAt).not.toEqual(0);
|
260
|
+
|
261
|
+
const updatedAt = text.$jazz.lastUpdatedAt;
|
262
|
+
|
263
|
+
await new Promise((r) => setTimeout(r, 10));
|
264
|
+
text.$jazz.applyDiff("Jane");
|
265
|
+
|
266
|
+
expect(text.$jazz.lastUpdatedAt).not.toEqual(updatedAt);
|
267
|
+
});
|
268
|
+
});
|
@@ -54,6 +54,7 @@ describe("createContext methods", () => {
|
|
54
54
|
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
55
55
|
crypto: Crypto,
|
56
56
|
sessionProvider: randomSessionProvider,
|
57
|
+
asActiveAccount: true,
|
57
58
|
});
|
58
59
|
|
59
60
|
expect(context.node).toBeDefined();
|
@@ -86,6 +87,7 @@ describe("createContext methods", () => {
|
|
86
87
|
crypto: Crypto,
|
87
88
|
AccountSchema: CustomAccount,
|
88
89
|
sessionProvider: randomSessionProvider,
|
90
|
+
asActiveAccount: true,
|
89
91
|
});
|
90
92
|
|
91
93
|
expect(context.account).toBeInstanceOf(
|
@@ -109,6 +111,7 @@ describe("createContext methods", () => {
|
|
109
111
|
crypto: Crypto,
|
110
112
|
sessionProvider: randomSessionProvider,
|
111
113
|
onLogOut,
|
114
|
+
asActiveAccount: true,
|
112
115
|
});
|
113
116
|
|
114
117
|
context.logOut();
|
@@ -131,6 +134,7 @@ describe("createContext methods", () => {
|
|
131
134
|
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
132
135
|
crypto: Crypto,
|
133
136
|
sessionProvider: randomSessionProvider,
|
137
|
+
asActiveAccount: true,
|
134
138
|
});
|
135
139
|
|
136
140
|
const loadedMap = await loadCoValueOrFail(context.node, coMap.id);
|
@@ -151,10 +155,30 @@ describe("createContext methods", () => {
|
|
151
155
|
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
152
156
|
crypto: Crypto,
|
153
157
|
sessionProvider: randomSessionProvider,
|
158
|
+
asActiveAccount: true,
|
154
159
|
});
|
155
160
|
|
156
161
|
expect(activeAccountContext.get()).toBe(context.account);
|
157
162
|
});
|
163
|
+
|
164
|
+
test("does not set the active account when asActiveAccount is false", async () => {
|
165
|
+
const account = await createJazzTestAccount({
|
166
|
+
isCurrentActiveAccount: false,
|
167
|
+
});
|
168
|
+
|
169
|
+
const context = await createJazzContextFromExistingCredentials({
|
170
|
+
credentials: {
|
171
|
+
accountID: account.$jazz.id,
|
172
|
+
secret: account.$jazz.localNode.getCurrentAgent().agentSecret,
|
173
|
+
},
|
174
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
175
|
+
crypto: Crypto,
|
176
|
+
sessionProvider: randomSessionProvider,
|
177
|
+
asActiveAccount: false,
|
178
|
+
});
|
179
|
+
|
180
|
+
expect(activeAccountContext.get()).not.toBe(context.account);
|
181
|
+
});
|
158
182
|
});
|
159
183
|
|
160
184
|
describe("createJazzContextForNewAccount", () => {
|
@@ -57,6 +57,7 @@ describe("Deep loading with depth arg", async () => {
|
|
57
57
|
sessionProvider: randomSessionProvider,
|
58
58
|
peersToLoadFrom: [initialAsPeer],
|
59
59
|
crypto: Crypto,
|
60
|
+
asActiveAccount: true,
|
60
61
|
});
|
61
62
|
|
62
63
|
const ownership = { owner: me };
|
@@ -287,6 +288,7 @@ test("Deep loading a record-like coMap", async () => {
|
|
287
288
|
sessionProvider: randomSessionProvider,
|
288
289
|
peersToLoadFrom: [initialAsPeer],
|
289
290
|
crypto: Crypto,
|
291
|
+
asActiveAccount: true,
|
290
292
|
});
|
291
293
|
|
292
294
|
const record = RecordLike.create(
|
@@ -1,6 +1,10 @@
|
|
1
1
|
import { assert, describe, expect, test } from "vitest";
|
2
2
|
import { Account, Group, co, z } from "../../exports";
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
createJazzTestAccount,
|
5
|
+
linkAccounts,
|
6
|
+
setupJazzTestSync,
|
7
|
+
} from "../../testing";
|
4
8
|
|
5
9
|
const RequestToJoin = co.map({
|
6
10
|
account: Account,
|
@@ -24,16 +28,20 @@ const Organization = co.map({
|
|
24
28
|
});
|
25
29
|
|
26
30
|
async function setup() {
|
31
|
+
await setupJazzTestSync();
|
32
|
+
|
27
33
|
const admin1 = await createJazzTestAccount();
|
28
34
|
const admin2 = await createJazzTestAccount();
|
29
35
|
const user1 = await createJazzTestAccount();
|
30
36
|
const user2 = await createJazzTestAccount();
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
await linkAccounts(admin1,
|
35
|
-
await linkAccounts(
|
36
|
-
await linkAccounts(
|
38
|
+
// TODO: with this setting the waitForAllCoValuesSync gets stuck
|
39
|
+
// https://github.com/garden-co/jazz/issues/2874
|
40
|
+
// await linkAccounts(admin1, admin2);
|
41
|
+
// await linkAccounts(admin1, user1);
|
42
|
+
// await linkAccounts(admin1, user2);
|
43
|
+
// await linkAccounts(admin2, user1);
|
44
|
+
// await linkAccounts(admin2, user2);
|
37
45
|
|
38
46
|
// The organization info are public
|
39
47
|
const adminsGroup = Group.create(admin1);
|
package/src/tools/tests/utils.ts
CHANGED
package/src/worker/index.ts
CHANGED
@@ -31,6 +31,10 @@ type WorkerOptions<
|
|
31
31
|
* If true, the inbox will not be loaded.
|
32
32
|
*/
|
33
33
|
skipInboxLoad?: boolean;
|
34
|
+
/**
|
35
|
+
* If false, the worker will not set in the global account context
|
36
|
+
*/
|
37
|
+
asActiveAccount?: boolean;
|
34
38
|
};
|
35
39
|
|
36
40
|
/** @category Context Creation */
|
@@ -45,6 +49,7 @@ export async function startWorker<
|
|
45
49
|
syncServer = "wss://cloud.jazz.tools",
|
46
50
|
AccountSchema = Account as unknown as S,
|
47
51
|
skipInboxLoad = false,
|
52
|
+
asActiveAccount = true,
|
48
53
|
} = options;
|
49
54
|
|
50
55
|
let node: LocalNode | undefined = undefined;
|
@@ -90,6 +95,7 @@ export async function startWorker<
|
|
90
95
|
sessionProvider: randomSessionProvider,
|
91
96
|
peersToLoadFrom,
|
92
97
|
crypto: options.crypto ?? (await WasmCrypto.create()),
|
98
|
+
asActiveAccount,
|
93
99
|
});
|
94
100
|
|
95
101
|
const account = context.account as InstanceOfSchema<S>;
|