jazz-tools 0.17.7 → 0.17.9
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 +35 -35
- package/CHANGELOG.md +21 -0
- package/dist/{chunk-SJHXI5AB.js → chunk-33NYHM7D.js} +18 -4
- package/dist/chunk-33NYHM7D.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/prosemirror/index.js +36 -12
- package/dist/prosemirror/index.js.map +1 -1
- package/dist/prosemirror/lib/sync.d.ts.map +1 -1
- package/dist/react/auth/Clerk.d.ts +2 -1
- package/dist/react/auth/Clerk.d.ts.map +1 -1
- package/dist/react/index.js +4 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/provider.d.ts +2 -1
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/coMap.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/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts +1 -1
- package/dist/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/prosemirror/lib/sync.ts +46 -16
- package/src/prosemirror/tests/plugin.test.ts +87 -31
- package/src/react/auth/Clerk.tsx +5 -3
- package/src/react/provider.tsx +3 -1
- package/src/tools/coValues/coMap.ts +5 -1
- package/src/tools/exports.ts +1 -0
- package/src/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.ts +7 -2
- package/src/tools/implementation/zodSchema/zodCo.ts +11 -0
- package/src/tools/implementation/zodSchema/zodReExport.ts +7 -2
- package/src/tools/tests/account.test.ts +6 -0
- package/src/tools/tests/coMap.test.ts +6 -0
- package/dist/chunk-SJHXI5AB.js.map +0 -1
@@ -1,29 +1,33 @@
|
|
1
1
|
// @vitest-environment jsdom
|
2
2
|
|
3
|
-
import {
|
3
|
+
import { CoRichText } from "jazz-tools";
|
4
4
|
import { createJazzTestAccount, setupJazzTestSync } from "jazz-tools/testing";
|
5
|
-
import { schema } from "prosemirror-schema-basic";
|
6
5
|
import { EditorState, TextSelection } from "prosemirror-state";
|
7
|
-
import { Plugin } from "prosemirror-state";
|
8
6
|
import { EditorView } from "prosemirror-view";
|
9
|
-
import {
|
7
|
+
import {
|
8
|
+
afterEach,
|
9
|
+
beforeEach,
|
10
|
+
describe,
|
11
|
+
expect,
|
12
|
+
it,
|
13
|
+
onTestFinished,
|
14
|
+
} from "vitest";
|
10
15
|
import { createJazzPlugin } from "../lib/plugin";
|
16
|
+
import { Schema } from "prosemirror-model";
|
17
|
+
import { schema as basicSchema } from "prosemirror-schema-basic";
|
18
|
+
import { addListNodes } from "prosemirror-schema-list";
|
11
19
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
let view: EditorView;
|
17
|
-
|
18
|
-
beforeEach(async () => {
|
19
|
-
await setupJazzTestSync();
|
20
|
-
account = await createJazzTestAccount({ isCurrentActiveAccount: true });
|
20
|
+
const schema = new Schema({
|
21
|
+
nodes: addListNodes(basicSchema.spec.nodes, "paragraph block*", "block"),
|
22
|
+
marks: basicSchema.spec.marks,
|
23
|
+
});
|
21
24
|
|
25
|
+
async function setupTest(initialContent = "<p>Hello</p>") {
|
22
26
|
// Create a real CoRichText with the test account as owner
|
23
|
-
coRichText = CoRichText.create(
|
27
|
+
const coRichText = CoRichText.create(initialContent);
|
24
28
|
|
25
|
-
plugin = createJazzPlugin(coRichText);
|
26
|
-
state = EditorState.create({
|
29
|
+
const plugin = createJazzPlugin(coRichText);
|
30
|
+
const state = EditorState.create({
|
27
31
|
schema,
|
28
32
|
plugins: [plugin],
|
29
33
|
});
|
@@ -33,25 +37,32 @@ beforeEach(async () => {
|
|
33
37
|
document.body.appendChild(editorElement);
|
34
38
|
|
35
39
|
// Initialize the editor view
|
36
|
-
view = new EditorView(editorElement, {
|
40
|
+
const view = new EditorView(editorElement, {
|
37
41
|
state,
|
38
42
|
});
|
39
|
-
});
|
40
43
|
|
41
|
-
|
42
|
-
// Clean up the editor view
|
43
|
-
if (view) {
|
44
|
+
onTestFinished(() => {
|
44
45
|
view.destroy();
|
45
|
-
|
46
|
-
}
|
46
|
+
editorElement.remove();
|
47
|
+
});
|
48
|
+
|
49
|
+
return { coRichText, plugin, state, view, editorElement };
|
50
|
+
}
|
51
|
+
|
52
|
+
beforeEach(async () => {
|
53
|
+
await setupJazzTestSync();
|
54
|
+
await createJazzTestAccount({ isCurrentActiveAccount: true });
|
47
55
|
});
|
48
56
|
|
49
57
|
describe("createJazzPlugin", () => {
|
50
|
-
it("initializes editor with CoRichText content", () => {
|
58
|
+
it("initializes editor with CoRichText content", async () => {
|
59
|
+
const { state } = await setupTest();
|
51
60
|
expect(state.doc.textContent).toContain("Hello");
|
52
61
|
});
|
53
62
|
|
54
63
|
it("updates editor when CoRichText changes", async () => {
|
64
|
+
const { coRichText, view } = await setupTest();
|
65
|
+
|
55
66
|
// Update CoRichText content
|
56
67
|
coRichText.applyDiff("<p>Updated content</p>");
|
57
68
|
|
@@ -61,7 +72,9 @@ describe("createJazzPlugin", () => {
|
|
61
72
|
expect(view.state.doc.textContent).toContain("Updated content");
|
62
73
|
});
|
63
74
|
|
64
|
-
it("updates CoRichText when editor content changes", () => {
|
75
|
+
it("updates CoRichText when editor content changes", async () => {
|
76
|
+
const { coRichText, view } = await setupTest();
|
77
|
+
|
65
78
|
// Create a transaction to update the editor content
|
66
79
|
const tr = view.state.tr.insertText(" World", 6);
|
67
80
|
view.dispatch(tr);
|
@@ -70,8 +83,8 @@ describe("createJazzPlugin", () => {
|
|
70
83
|
expect(coRichText.toString()).toContain("Hello World");
|
71
84
|
});
|
72
85
|
|
73
|
-
it("handles empty CoRichText initialization", () => {
|
74
|
-
const emptyCoRichText = CoRichText.create(""
|
86
|
+
it("handles empty CoRichText initialization", async () => {
|
87
|
+
const emptyCoRichText = CoRichText.create("");
|
75
88
|
const emptyPlugin = createJazzPlugin(emptyCoRichText);
|
76
89
|
const emptyState = EditorState.create({
|
77
90
|
schema,
|
@@ -81,7 +94,7 @@ describe("createJazzPlugin", () => {
|
|
81
94
|
expect(emptyState.doc.textContent).toBe("");
|
82
95
|
});
|
83
96
|
|
84
|
-
it("handles undefined CoRichText", () => {
|
97
|
+
it("handles undefined CoRichText", async () => {
|
85
98
|
const undefinedPlugin = createJazzPlugin(undefined);
|
86
99
|
const undefinedState = EditorState.create({
|
87
100
|
schema,
|
@@ -91,7 +104,9 @@ describe("createJazzPlugin", () => {
|
|
91
104
|
expect(undefinedState.doc.textContent).toBe("");
|
92
105
|
});
|
93
106
|
|
94
|
-
it("prevents infinite update loops", () => {
|
107
|
+
it("prevents infinite update loops", async () => {
|
108
|
+
const { coRichText, view } = await setupTest();
|
109
|
+
|
95
110
|
// Create a transaction that would normally trigger a CoRichText update
|
96
111
|
const tr = view.state.tr.insertText(" Loop", 6);
|
97
112
|
|
@@ -106,7 +121,9 @@ describe("createJazzPlugin", () => {
|
|
106
121
|
expect(coRichText.toString()).not.toContain("Loop");
|
107
122
|
});
|
108
123
|
|
109
|
-
it
|
124
|
+
it("preserves selection when CoRichText changes", async () => {
|
125
|
+
const { coRichText, view } = await setupTest();
|
126
|
+
|
110
127
|
// Set a selection in the editor
|
111
128
|
const tr = view.state.tr.setSelection(
|
112
129
|
TextSelection.create(view.state.doc, 2, 5),
|
@@ -118,10 +135,49 @@ describe("createJazzPlugin", () => {
|
|
118
135
|
expect(view.state.selection.to).toBe(5);
|
119
136
|
|
120
137
|
// Update CoRichText content
|
121
|
-
coRichText.applyDiff("<p>
|
138
|
+
coRichText.applyDiff("<p>Hello world</p>");
|
139
|
+
|
140
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
122
141
|
|
123
142
|
// Verify selection is preserved after content update
|
124
143
|
expect(view.state.selection.from).toBe(2);
|
125
144
|
expect(view.state.selection.to).toBe(5);
|
126
145
|
});
|
146
|
+
|
147
|
+
it("falls back to creating a new EditorState when the transform fails", async () => {
|
148
|
+
const { coRichText, editorElement } = await setupTest(
|
149
|
+
"<p>A <strong>hu<em>man</strong></em>.</p>",
|
150
|
+
);
|
151
|
+
|
152
|
+
// Wait for the next tick to allow the update to propagate
|
153
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
154
|
+
|
155
|
+
// Update CoRichText content
|
156
|
+
coRichText.applyDiff(
|
157
|
+
"<ol><li><p>A <strong>hu</strong><em><strong>man</strong></em>.</p></li></ol>",
|
158
|
+
);
|
159
|
+
|
160
|
+
// Wait for the next tick to allow the update to propagate
|
161
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
162
|
+
|
163
|
+
expect(editorElement.querySelector(".ProseMirror")?.innerHTML).toBe(
|
164
|
+
"<ol><li><p>A <strong>hu</strong><em><strong>man</strong></em>.</p></li></ol>",
|
165
|
+
);
|
166
|
+
});
|
167
|
+
|
168
|
+
it("handles updates with emojis", async () => {
|
169
|
+
const { coRichText, editorElement } = await setupTest(
|
170
|
+
"<p>A <strong>hu</strong><em><strong>man</strong></em>.</p>",
|
171
|
+
);
|
172
|
+
|
173
|
+
// Update CoRichText content
|
174
|
+
coRichText.applyDiff("<p>A human💪</p>");
|
175
|
+
|
176
|
+
// Wait for the next tick to allow the update to propagate
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
178
|
+
|
179
|
+
expect(editorElement.querySelector(".ProseMirror")?.innerHTML).toBe(
|
180
|
+
"<p>A human💪</p>",
|
181
|
+
);
|
182
|
+
});
|
127
183
|
});
|
package/src/react/auth/Clerk.tsx
CHANGED
@@ -9,7 +9,7 @@ import {
|
|
9
9
|
} from "jazz-tools";
|
10
10
|
import { LocalStorageKVStore } from "jazz-tools/browser";
|
11
11
|
import { useAuthSecretStorage, useJazzContext } from "jazz-tools/react-core";
|
12
|
-
import { useEffect, useMemo, useState } from "react";
|
12
|
+
import { ReactNode, useEffect, useMemo, useState } from "react";
|
13
13
|
import { JazzProviderProps, JazzReactProvider } from "../provider.js";
|
14
14
|
|
15
15
|
function useJazzClerkAuth(clerk: MinimalClerkClient) {
|
@@ -43,7 +43,9 @@ export const JazzReactProviderWithClerk = <
|
|
43
43
|
| (AccountClass<Account> & CoValueFromRaw<Account>)
|
44
44
|
| AnyAccountSchema,
|
45
45
|
>(
|
46
|
-
props: {
|
46
|
+
props: {
|
47
|
+
clerk: MinimalClerkClient;
|
48
|
+
} & JazzProviderProps<S>,
|
47
49
|
) => {
|
48
50
|
const [isLoaded, setIsLoaded] = useState(false);
|
49
51
|
|
@@ -61,7 +63,7 @@ export const JazzReactProviderWithClerk = <
|
|
61
63
|
}, []);
|
62
64
|
|
63
65
|
if (!isLoaded) {
|
64
|
-
return null;
|
66
|
+
return props.fallback ?? null;
|
65
67
|
}
|
66
68
|
|
67
69
|
return (
|
package/src/react/provider.tsx
CHANGED
@@ -20,6 +20,7 @@ export type JazzProviderProps<
|
|
20
20
|
> = {
|
21
21
|
children: React.ReactNode;
|
22
22
|
enableSSR?: boolean;
|
23
|
+
fallback?: React.ReactNode | null;
|
23
24
|
} & JazzContextManagerProps<S>;
|
24
25
|
|
25
26
|
/** @category Context & Hooks */
|
@@ -38,6 +39,7 @@ export function JazzReactProvider<
|
|
38
39
|
logOutReplacement,
|
39
40
|
onAnonymousAccountDiscarded,
|
40
41
|
enableSSR,
|
42
|
+
fallback = null,
|
41
43
|
}: JazzProviderProps<S>) {
|
42
44
|
const [contextManager] = React.useState(
|
43
45
|
() =>
|
@@ -100,7 +102,7 @@ export function JazzReactProvider<
|
|
100
102
|
return (
|
101
103
|
<JazzContext.Provider value={value}>
|
102
104
|
<JazzContextManagerContext.Provider value={contextManager}>
|
103
|
-
{value
|
105
|
+
{value ? children : fallback}
|
104
106
|
</JazzContextManagerContext.Provider>
|
105
107
|
</JazzContext.Provider>
|
106
108
|
);
|
@@ -628,7 +628,11 @@ export class CoMap extends CoValueBase implements CoValue {
|
|
628
628
|
resolve?: RefsToResolveStrict<M, R>;
|
629
629
|
},
|
630
630
|
): Promise<Resolved<M, R> | null> {
|
631
|
-
|
631
|
+
const mapId = CoMap._findUnique(
|
632
|
+
options.unique,
|
633
|
+
options.owner.id,
|
634
|
+
options.owner._loadedAs,
|
635
|
+
);
|
632
636
|
let map: Resolved<M, R> | null = await loadCoValueWithoutMe(this, mapId, {
|
633
637
|
...options,
|
634
638
|
loadAs: options.owner._loadedAs,
|
package/src/tools/exports.ts
CHANGED
@@ -38,9 +38,14 @@ import {
|
|
38
38
|
// Note: if you're editing this function, edit the `isAnyCoValueSchema`
|
39
39
|
// function in `zodReExport.ts` as well
|
40
40
|
export function isAnyCoValueSchema(
|
41
|
-
schema:
|
41
|
+
schema: unknown,
|
42
42
|
): schema is AnyCoreCoValueSchema {
|
43
|
-
return
|
43
|
+
return (
|
44
|
+
typeof schema === "object" &&
|
45
|
+
schema !== null &&
|
46
|
+
"collaborative" in schema &&
|
47
|
+
schema.collaborative === true
|
48
|
+
);
|
44
49
|
}
|
45
50
|
|
46
51
|
export function isCoValueSchema(
|
@@ -19,6 +19,7 @@ import {
|
|
19
19
|
createCoreCoPlainTextSchema,
|
20
20
|
createCoreFileStreamSchema,
|
21
21
|
hydrateCoreCoValueSchema,
|
22
|
+
isAnyCoValueSchema,
|
22
23
|
} from "../../internal.js";
|
23
24
|
import {
|
24
25
|
CoDiscriminatedUnionSchema,
|
@@ -36,6 +37,11 @@ import { z } from "./zodReExport.js";
|
|
36
37
|
export const coMapDefiner = <Shape extends z.core.$ZodLooseShape>(
|
37
38
|
shape: Shape,
|
38
39
|
): CoMapSchema<Shape> => {
|
40
|
+
if (isAnyCoValueSchema(shape as any)) {
|
41
|
+
throw new Error(
|
42
|
+
"co.map() expects an object as its argument, not a CoValue schema",
|
43
|
+
);
|
44
|
+
}
|
39
45
|
const coreSchema = createCoreCoMapSchema(shape);
|
40
46
|
return hydrateCoreCoValueSchema(coreSchema);
|
41
47
|
};
|
@@ -116,6 +122,11 @@ export const coProfileDefiner = <
|
|
116
122
|
>(
|
117
123
|
shape: Shape & Partial<DefaultProfileShape> = {} as any,
|
118
124
|
): CoProfileSchema<Shape> => {
|
125
|
+
if (isAnyCoValueSchema(shape as any)) {
|
126
|
+
throw new Error(
|
127
|
+
"co.profile() expects an object as its argument, not a CoValue schema",
|
128
|
+
);
|
129
|
+
}
|
119
130
|
const ehnancedShape = Object.assign(shape, {
|
120
131
|
name: z.string(),
|
121
132
|
inbox: z.optional(z.string()),
|
@@ -88,6 +88,11 @@ function containsCoValueSchema(shape?: core.$ZodLooseShape): boolean {
|
|
88
88
|
|
89
89
|
// Note: if you're editing this function, edit the `isAnyCoValueSchema`
|
90
90
|
// function in `zodSchemaToCoSchema.ts` as well
|
91
|
-
function isAnyCoValueSchema(schema:
|
92
|
-
return
|
91
|
+
function isAnyCoValueSchema(schema: unknown): boolean {
|
92
|
+
return (
|
93
|
+
typeof schema === "object" &&
|
94
|
+
schema !== null &&
|
95
|
+
"collaborative" in schema &&
|
96
|
+
schema.collaborative === true
|
97
|
+
);
|
93
98
|
}
|
@@ -137,6 +137,12 @@ test("loading raw accounts should work", async () => {
|
|
137
137
|
expect(loadedAccount.profile!.name).toBe("test 1");
|
138
138
|
});
|
139
139
|
|
140
|
+
test("co.profile() should throw an error if passed a CoValue schema", async () => {
|
141
|
+
expect(() => co.profile(co.map({}))).toThrow(
|
142
|
+
"co.profile() expects an object as its argument, not a CoValue schema",
|
143
|
+
);
|
144
|
+
});
|
145
|
+
|
140
146
|
test("should support recursive props on co.profile", async () => {
|
141
147
|
const User = co.profile({
|
142
148
|
name: z.string(),
|
@@ -2325,6 +2325,12 @@ describe("co.map schema", () => {
|
|
2325
2325
|
expect(draftPerson.extraField).toEqual("extra");
|
2326
2326
|
});
|
2327
2327
|
});
|
2328
|
+
|
2329
|
+
test("co.map() should throw an error if passed a CoValue schema", () => {
|
2330
|
+
expect(() => co.map(co.map({}))).toThrow(
|
2331
|
+
"co.map() expects an object as its argument, not a CoValue schema",
|
2332
|
+
);
|
2333
|
+
});
|
2328
2334
|
});
|
2329
2335
|
|
2330
2336
|
describe("Updating a nested reference", () => {
|