community-jazz-vue 0.15.4

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.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +21 -0
  2. package/CHANGELOG.md +2201 -0
  3. package/LICENSE.txt +19 -0
  4. package/README.md +26 -0
  5. package/dist/Image.vue.d.ts +26 -0
  6. package/dist/auth/JazzVueProviderWithClerk.d.ts +83 -0
  7. package/dist/auth/PasskeyAuthBasicUI.vue.d.ts +21 -0
  8. package/dist/auth/useClerkAuth.d.ts +2 -0
  9. package/dist/auth/useIsAuthenticated.d.ts +1 -0
  10. package/dist/auth/usePasskeyAuth.d.ts +18 -0
  11. package/dist/auth/usePassphraseAuth.d.ts +20 -0
  12. package/dist/composables.d.ts +22 -0
  13. package/dist/index.d.ts +11 -0
  14. package/dist/index.js +667 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/provider-CCZVJj45.js +143 -0
  17. package/dist/provider-CCZVJj45.js.map +1 -0
  18. package/dist/provider.d.ts +172 -0
  19. package/dist/testing.d.ts +30 -0
  20. package/dist/testing.js +41 -0
  21. package/dist/testing.js.map +1 -0
  22. package/dist/tests/fixtures.d.ts +1 -0
  23. package/dist/tests/proxyBehavior.test.d.ts +1 -0
  24. package/dist/tests/testUtils.d.ts +9 -0
  25. package/dist/tests/useAcceptInvite.test.d.ts +1 -0
  26. package/dist/tests/useAccount.test.d.ts +1 -0
  27. package/dist/tests/useCoState.test.d.ts +1 -0
  28. package/dist/tests/useInboxSender.test.d.ts +1 -0
  29. package/dist/tests/useIsAuthenticated.test.d.ts +1 -0
  30. package/dist/tests/usePassphraseAuth.test.d.ts +1 -0
  31. package/dist/utils/contextManager.d.ts +3 -0
  32. package/package.json +53 -0
  33. package/src/Image.vue +151 -0
  34. package/src/auth/JazzVueProviderWithClerk.ts +123 -0
  35. package/src/auth/PasskeyAuthBasicUI.vue +153 -0
  36. package/src/auth/useClerkAuth.ts +35 -0
  37. package/src/auth/useIsAuthenticated.ts +21 -0
  38. package/src/auth/usePasskeyAuth.ts +48 -0
  39. package/src/auth/usePassphraseAuth.ts +57 -0
  40. package/src/composables.ts +323 -0
  41. package/src/index.ts +14 -0
  42. package/src/provider.ts +192 -0
  43. package/src/testing.ts +45 -0
  44. package/src/tests/fixtures.ts +2050 -0
  45. package/src/tests/proxyBehavior.test.ts +267 -0
  46. package/src/tests/testUtils.ts +75 -0
  47. package/src/tests/useAcceptInvite.test.ts +55 -0
  48. package/src/tests/useAccount.test.ts +59 -0
  49. package/src/tests/useCoState.test.ts +175 -0
  50. package/src/tests/useInboxSender.test.ts +58 -0
  51. package/src/tests/useIsAuthenticated.test.ts +35 -0
  52. package/src/tests/usePassphraseAuth.test.ts +95 -0
  53. package/src/utils/contextManager.ts +31 -0
  54. package/src/vite-env.d.ts +7 -0
  55. package/tsconfig.json +20 -0
  56. package/vite.config.ts +31 -0
@@ -0,0 +1,323 @@
1
+ import {
2
+ Account,
3
+ AccountClass,
4
+ AnonymousJazzAgent,
5
+ AnyAccountSchema,
6
+ type AuthSecretStorage,
7
+ type CoValue,
8
+ CoValueClassOrSchema,
9
+ InboxSender,
10
+ InstanceOfSchema,
11
+ type JazzAuthContext,
12
+ JazzContextManager,
13
+ type JazzContextType,
14
+ type Loaded,
15
+ type RefsToResolve,
16
+ ResolveQuery,
17
+ ResolveQueryStrict,
18
+ coValueClassFromCoValueClassOrSchema,
19
+ subscribeToCoValue,
20
+ } from "jazz-tools";
21
+ import { consumeInviteLinkFromWindowLocation } from "jazz-tools/browser";
22
+ import {
23
+ type ComputedRef,
24
+ type Ref,
25
+ type ShallowRef,
26
+ computed,
27
+ inject,
28
+ markRaw,
29
+ nextTick,
30
+ onMounted,
31
+ onUnmounted,
32
+ ref,
33
+ shallowRef,
34
+ toRaw,
35
+ watch,
36
+ } from "vue";
37
+
38
+ import {
39
+ JazzAuthContextSymbol,
40
+ JazzContextManagerSymbol,
41
+ JazzContextSymbol,
42
+ } from "./provider.js";
43
+
44
+ export const logoutHandler = ref<() => void>();
45
+
46
+ function getCurrentAccountFromContextManager<Acc extends Account>(
47
+ contextManager: JazzContextManager<Acc, any>,
48
+ ) {
49
+ const context = contextManager.getCurrentValue();
50
+
51
+ if (!context) {
52
+ throw new Error("No context found");
53
+ }
54
+
55
+ return "me" in context ? context.me : context.guest;
56
+ }
57
+
58
+ export function useJazzContext<Acc extends Account>(): Ref<
59
+ JazzContextType<Acc>,
60
+ JazzContextType<Acc>
61
+ > {
62
+ const context = inject<Ref<JazzContextType<Acc>>>(JazzContextSymbol);
63
+ if (!context) {
64
+ throw new Error("useJazzContext must be used within a JazzProvider");
65
+ }
66
+ return context;
67
+ }
68
+
69
+ export function useJazzContextManager<Acc extends Account>() {
70
+ const context = inject<Ref<JazzContextManager<Acc, {}>>>(
71
+ JazzContextManagerSymbol,
72
+ );
73
+
74
+ if (!context?.value) {
75
+ throw new Error(
76
+ "You need to set up a JazzProvider on top of your app to use this hook.",
77
+ );
78
+ }
79
+
80
+ return context;
81
+ }
82
+
83
+ export function useAuthSecretStorage() {
84
+ const context = inject<AuthSecretStorage>(JazzAuthContextSymbol);
85
+ if (!context) {
86
+ throw new Error("useAuthSecretStorage must be used within a JazzProvider");
87
+ }
88
+ return context;
89
+ }
90
+
91
+ export function useAccount<
92
+ A extends AccountClass<Account> | AnyAccountSchema = typeof Account,
93
+ R extends ResolveQuery<A> = true,
94
+ >(
95
+ AccountSchema: A = Account as unknown as A,
96
+ options?: {
97
+ resolve?: ResolveQueryStrict<A, R>;
98
+ },
99
+ ): {
100
+ me: ComputedRef<Loaded<A, R> | undefined | null>;
101
+ agent: AnonymousJazzAgent | Loaded<A, true>;
102
+ logOut: () => void;
103
+ } {
104
+ const context = useJazzContext();
105
+ const contextManager = useJazzContextManager<InstanceOfSchema<A>>();
106
+
107
+ if (!context.value) {
108
+ throw new Error("useAccount must be used within a JazzProvider");
109
+ }
110
+
111
+ const agent = getCurrentAccountFromContextManager(contextManager.value);
112
+
113
+ // Handle guest mode - return null for me and the guest agent
114
+ if (!("me" in context.value)) {
115
+ return {
116
+ me: computed(() => null) as any,
117
+ agent: agent,
118
+ logOut: context.value.logOut,
119
+ };
120
+ }
121
+
122
+ const contextMe = context.value.me as InstanceOfSchema<A>;
123
+
124
+ const me = useCoState(AccountSchema as any, contextMe.id, options as any);
125
+
126
+ return {
127
+ me: computed(() => {
128
+ const value =
129
+ options?.resolve === undefined ? me.value || contextMe : me.value;
130
+ return value ? markRaw(value) : value;
131
+ }) as any,
132
+ agent: agent,
133
+ logOut: context.value.logOut,
134
+ };
135
+ }
136
+
137
+ export function useCoState<
138
+ S extends CoValueClassOrSchema,
139
+ const R extends RefsToResolve<S> = true,
140
+ >(
141
+ Schema: S,
142
+ id: string | undefined,
143
+ options?: { resolve?: ResolveQueryStrict<S, R> },
144
+ ): Ref<Loaded<S, R> | undefined | null> {
145
+ const state: ShallowRef<Loaded<S, R> | undefined | null> =
146
+ shallowRef(undefined);
147
+ const context = useJazzContext();
148
+
149
+ if (!context.value) {
150
+ throw new Error("useCoState must be used within a JazzProvider");
151
+ }
152
+
153
+ let unsubscribe: (() => void) | undefined;
154
+
155
+ watch(
156
+ [() => id, context],
157
+ ([currentId, currentContext]) => {
158
+ if (unsubscribe) {
159
+ unsubscribe();
160
+ unsubscribe = undefined;
161
+ }
162
+
163
+ if (!currentId || !currentContext) {
164
+ state.value = undefined;
165
+ return;
166
+ }
167
+
168
+ const loadAsAgent =
169
+ "me" in currentContext ? currentContext.me : currentContext.guest;
170
+ if (!loadAsAgent) {
171
+ state.value = undefined;
172
+ return;
173
+ }
174
+
175
+ const safeLoadAsAgent = toRaw(loadAsAgent);
176
+
177
+ try {
178
+ unsubscribe = subscribeToCoValue(
179
+ coValueClassFromCoValueClassOrSchema(Schema),
180
+ currentId as any,
181
+ {
182
+ resolve: options?.resolve as any,
183
+ loadAs: safeLoadAsAgent,
184
+ onUnavailable: () => {
185
+ state.value = null;
186
+ },
187
+ onUnauthorized: () => {
188
+ state.value = null;
189
+ },
190
+ syncResolution: true,
191
+ },
192
+ (value: any) => {
193
+ // Use markRaw to prevent Vue from making Jazz objects reactive
194
+ // but still allow property access and mutations
195
+ state.value = value ? markRaw(value) : value;
196
+ },
197
+ );
198
+ } catch (error) {
199
+ console.error("Error in useCoState subscription:", error);
200
+ state.value = null;
201
+ }
202
+ },
203
+ { immediate: true },
204
+ );
205
+
206
+ onUnmounted(() => {
207
+ if (unsubscribe) {
208
+ unsubscribe();
209
+ unsubscribe = undefined;
210
+ }
211
+ });
212
+
213
+ return state;
214
+ }
215
+
216
+ export function useAcceptInvite<S extends CoValueClassOrSchema>({
217
+ invitedObjectSchema,
218
+ onAccept,
219
+ forValueHint,
220
+ }: {
221
+ invitedObjectSchema: S;
222
+ onAccept: (projectID: string) => void;
223
+ forValueHint?: string;
224
+ }): void {
225
+ const context = useJazzContext();
226
+
227
+ if (!context.value) {
228
+ throw new Error("useAcceptInvite must be used within a JazzProvider");
229
+ }
230
+
231
+ if (!("me" in context.value)) {
232
+ throw new Error(
233
+ "useAcceptInvite can't be used in a JazzProvider with auth === 'guest'.",
234
+ );
235
+ }
236
+
237
+ const handleInvite = () => {
238
+ const result = consumeInviteLinkFromWindowLocation({
239
+ as: toRaw((context.value as JazzAuthContext<Account>).me),
240
+ invitedObjectSchema,
241
+ forValueHint,
242
+ });
243
+
244
+ result
245
+ .then((res) => res && onAccept(res.valueID))
246
+ .catch((e) => {
247
+ console.error("Failed to accept invite", e);
248
+ });
249
+ };
250
+
251
+ onMounted(() => {
252
+ handleInvite();
253
+ window.addEventListener("hashchange", handleInvite);
254
+ });
255
+
256
+ onUnmounted(() => {
257
+ window.removeEventListener("hashchange", handleInvite);
258
+ });
259
+
260
+ watch(
261
+ () => onAccept,
262
+ (newOnAccept, oldOnAccept) => {
263
+ if (newOnAccept !== oldOnAccept) {
264
+ handleInvite();
265
+ }
266
+ },
267
+ );
268
+ }
269
+
270
+ export function experimental_useInboxSender<
271
+ I extends CoValue,
272
+ O extends CoValue | undefined,
273
+ >(inboxOwnerID: string | undefined) {
274
+ const context = useJazzContext();
275
+
276
+ if (!context.value) {
277
+ throw new Error(
278
+ "experimental_useInboxSender must be used within a JazzProvider",
279
+ );
280
+ }
281
+
282
+ if (!("me" in context.value)) {
283
+ throw new Error(
284
+ "experimental_useInboxSender can't be used in a JazzProvider with auth === 'guest'.",
285
+ );
286
+ }
287
+
288
+ const me = computed(() => (context.value as JazzAuthContext<Account>).me);
289
+ const inboxRef = ref<Promise<InboxSender<I, O>> | undefined>(undefined);
290
+
291
+ const sendMessage = async (message: I) => {
292
+ if (!inboxOwnerID) throw new Error("Inbox owner ID is required");
293
+
294
+ if (!inboxRef.value) {
295
+ const inbox = InboxSender.load<I, O>(inboxOwnerID, toRaw(me.value));
296
+ inboxRef.value = inbox;
297
+ }
298
+
299
+ let inbox = await inboxRef.value;
300
+
301
+ if (inbox.owner.id !== inboxOwnerID) {
302
+ const req = InboxSender.load<I, O>(inboxOwnerID, toRaw(me.value));
303
+ inboxRef.value = req;
304
+ inbox = await req;
305
+ }
306
+
307
+ return inbox.sendMessage(message);
308
+ };
309
+
310
+ watch(
311
+ () => inboxOwnerID,
312
+ () => {
313
+ inboxRef.value = undefined;
314
+ },
315
+ );
316
+
317
+ return sendMessage;
318
+ }
319
+
320
+ // useAccountOrGuest has been removed in v0.15.4 to match React library.
321
+ // It has been merged into useAccount which now handles both authenticated and guest scenarios.
322
+ // This change maintains 1:1 API compatibility with the React Jazz library.
323
+ // If you were using useAccountOrGuest, please migrate to useAccount.
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./composables.js";
2
+ export { JazzProvider, JazzVueProvider } from "./provider.js";
3
+
4
+ export * from "./auth/usePassphraseAuth.js";
5
+ export * from "./auth/useIsAuthenticated.js";
6
+ export * from "./auth/usePasskeyAuth.js";
7
+ export * from "./auth/useClerkAuth.js";
8
+ export { JazzVueProviderWithClerk } from "./auth/JazzVueProviderWithClerk.js";
9
+ export { default as PasskeyAuthBasicUI } from "./auth/PasskeyAuthBasicUI.vue";
10
+
11
+ export { default as Image } from "./Image.vue";
12
+
13
+ export { createInviteLink, parseInviteLink } from "jazz-tools/browser";
14
+ export { createImage } from "jazz-tools/media";
@@ -0,0 +1,192 @@
1
+ import type {
2
+ Account,
3
+ AccountClass,
4
+ AnyAccountSchema,
5
+ CoValueFromRaw,
6
+ JazzContextType,
7
+ SyncConfig,
8
+ } from "jazz-tools";
9
+ import { JazzBrowserContextManager } from "jazz-tools/browser";
10
+ import {
11
+ type PropType,
12
+ defineComponent,
13
+ markRaw,
14
+ nextTick,
15
+ onUnmounted,
16
+ provide,
17
+ ref,
18
+ shallowRef,
19
+ toRaw,
20
+ watch,
21
+ } from "vue";
22
+
23
+ export const logoutHandler = ref<() => void>();
24
+
25
+ export const JazzContextSymbol = Symbol("JazzContext");
26
+ export const JazzContextManagerSymbol = Symbol("JazzContextManager");
27
+ export const JazzAuthContextSymbol = Symbol("JazzAuthContext");
28
+
29
+ export const JazzVueProvider = defineComponent({
30
+ name: "JazzProvider",
31
+ props: {
32
+ AccountSchema: {
33
+ type: [Function, Object] as unknown as PropType<
34
+ (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema
35
+ >,
36
+ required: false,
37
+ },
38
+ guestMode: {
39
+ type: Boolean,
40
+ default: false,
41
+ },
42
+ sync: {
43
+ type: Object as PropType<SyncConfig>,
44
+ required: true,
45
+ },
46
+ storage: {
47
+ type: String as PropType<"indexedDB">,
48
+ default: undefined,
49
+ },
50
+ defaultProfileName: {
51
+ type: String,
52
+ required: false,
53
+ },
54
+ onLogOut: {
55
+ type: Function as PropType<() => void>,
56
+ required: false,
57
+ },
58
+ logOutReplacement: {
59
+ type: Function as PropType<() => void>,
60
+ required: false,
61
+ },
62
+ onAnonymousAccountDiscarded: {
63
+ type: Function as PropType<(anonymousAccount: Account) => Promise<void>>,
64
+ required: false,
65
+ },
66
+ enableSSR: {
67
+ type: Boolean,
68
+ default: false,
69
+ },
70
+ },
71
+ setup(props, { slots }) {
72
+ const contextManager = markRaw(
73
+ new JazzBrowserContextManager<
74
+ (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema
75
+ >({
76
+ useAnonymousFallback: props.enableSSR,
77
+ }),
78
+ );
79
+
80
+ // Use shallowRef to avoid deep reactivity on Jazz objects
81
+ const ctx = shallowRef<JazzContextType<any>>();
82
+
83
+ provide(JazzContextSymbol, ctx);
84
+ provide(JazzContextManagerSymbol, ref(contextManager));
85
+ provide(
86
+ JazzAuthContextSymbol,
87
+ markRaw(contextManager.getAuthSecretStorage()),
88
+ );
89
+
90
+ // Simple context creation flag to prevent rapid recreation
91
+ let contextCreationInProgress = false;
92
+
93
+ // Watch for prop changes and recreate context when needed
94
+ watch(
95
+ [
96
+ () => props.sync.peer,
97
+ () => props.sync.when,
98
+ () => props.storage,
99
+ () => props.guestMode,
100
+ () => props.AccountSchema,
101
+ ],
102
+ async (newValues, oldValues) => {
103
+ // Prevent rapid re-creation during initialization
104
+ if (contextCreationInProgress) return;
105
+
106
+ // Efficient O(1) change detection - skip if no actual changes
107
+ if (
108
+ oldValues &&
109
+ newValues[0] === oldValues[0] && // sync.peer
110
+ newValues[1] === oldValues[1] && // sync.when
111
+ newValues[2] === oldValues[2] && // storage
112
+ newValues[3] === oldValues[3] && // guestMode
113
+ newValues[4] === oldValues[4] // AccountSchema
114
+ ) {
115
+ return;
116
+ }
117
+
118
+ contextCreationInProgress = true;
119
+
120
+ try {
121
+ await contextManager.createContext({
122
+ AccountSchema: props.AccountSchema,
123
+ guestMode: props.guestMode,
124
+ sync: props.sync,
125
+ storage: props.storage,
126
+ defaultProfileName: props.defaultProfileName,
127
+ onLogOut: props.onLogOut,
128
+ logOutReplacement: props.logOutReplacement,
129
+ onAnonymousAccountDiscarded: props.onAnonymousAccountDiscarded,
130
+ });
131
+ } catch (error) {
132
+ console.error("Error creating Jazz browser context:", error);
133
+ } finally {
134
+ contextCreationInProgress = false;
135
+ }
136
+ },
137
+ { immediate: true },
138
+ );
139
+
140
+ // Set up context manager subscription with complete isolation
141
+ let lastValue: any = undefined;
142
+ let isUpdating = false;
143
+
144
+ const cleanup = contextManager.subscribe(() => {
145
+ if (isUpdating) return;
146
+
147
+ isUpdating = true;
148
+ try {
149
+ const rawContextManager = toRaw(contextManager);
150
+ let currentValue = rawContextManager.getCurrentValue();
151
+
152
+ if (currentValue === lastValue) return;
153
+
154
+ // Use markRaw to prevent Vue reactivity
155
+ if (currentValue && typeof currentValue === "object") {
156
+ currentValue = markRaw(toRaw(currentValue));
157
+ }
158
+
159
+ lastValue = currentValue;
160
+
161
+ ctx.value = currentValue;
162
+ } catch (error) {
163
+ console.error("Error in context manager subscription:", error);
164
+ ctx.value = undefined;
165
+ } finally {
166
+ isUpdating = false;
167
+ }
168
+ });
169
+
170
+ onUnmounted(() => {
171
+ cleanup();
172
+ });
173
+
174
+ onUnmounted(() => {
175
+ if (ctx.value) ctx.value.done?.();
176
+
177
+ // Only call done() in production, not in development (for HMR)
178
+ if (process.env.NODE_ENV !== "development") {
179
+ contextManager.done();
180
+ }
181
+ });
182
+
183
+ // Simple render function - render children when context exists
184
+ return () => (ctx.value ? slots.default?.() : null);
185
+ },
186
+ });
187
+
188
+ /**
189
+ * Alias to JazzVueProvider for backward compatibility
190
+ * @deprecated Use JazzVueProvider instead
191
+ */
192
+ export const JazzProvider = JazzVueProvider;
package/src/testing.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { Account, AnonymousJazzAgent } from "jazz-tools";
2
+ import { TestJazzContextManager } from "jazz-tools/testing";
3
+ import { provide } from "vue";
4
+ import { type PropType, defineComponent, ref } from "vue";
5
+ import {
6
+ JazzAuthContextSymbol,
7
+ JazzContextManagerSymbol,
8
+ JazzContextSymbol,
9
+ } from "./provider.js";
10
+
11
+ export const JazzTestProvider = defineComponent({
12
+ name: "JazzTestProvider",
13
+ props: {
14
+ account: {
15
+ type: Object as PropType<Account | { guest: AnonymousJazzAgent }>,
16
+ required: false,
17
+ },
18
+ isAuthenticated: {
19
+ type: Boolean,
20
+ required: false,
21
+ },
22
+ },
23
+ setup(props, { slots }) {
24
+ const contextManager = TestJazzContextManager.fromAccountOrGuest(
25
+ props.account,
26
+ {
27
+ isAuthenticated: props.isAuthenticated,
28
+ },
29
+ );
30
+
31
+ provide(JazzContextSymbol, ref(contextManager.getCurrentValue()));
32
+ provide(JazzContextManagerSymbol, ref(contextManager));
33
+ provide(JazzAuthContextSymbol, contextManager.getAuthSecretStorage());
34
+
35
+ return () => slots.default?.();
36
+ },
37
+ });
38
+
39
+ export {
40
+ createJazzTestAccount,
41
+ createJazzTestGuest,
42
+ linkAccounts,
43
+ setActiveAccount,
44
+ setupJazzTestSync,
45
+ } from "jazz-tools/testing";