jazz-svelte 0.0.1

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/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright 2024, Garden Computing, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Jazz Svelte
2
+
3
+ > Modern Svelte bindings for Jazz 🎵
4
+
5
+ ## ⚠️ Experimental Status
6
+
7
+ <div align="center">
8
+
9
+ ```
10
+ 🐉 Here be dragons! 🐉
11
+ ```
12
+
13
+ </div>
14
+
15
+ ### 🚧 Development Notice
16
+
17
+ Greetings, brave explorer! 🌟 You've caught us in our early stages - this package is currently experimental and under active development.
18
+
19
+ ---
20
+
21
+ > **Note**: You've been warned, but we're glad you're here! Let's build something amazing together. 🚀
@@ -0,0 +1,69 @@
1
+ <script lang="ts" generics="Acc extends Account">
2
+ import { createJazzBrowserContext } from "jazz-browser";
3
+ import type { AccountClass, AuthMethod } from "jazz-tools";
4
+ import { Account } from "jazz-tools";
5
+ import { type Snippet, setContext, untrack } from "svelte";
6
+ import { JAZZ_CTX, type JazzContext } from "./jazz.svelte";
7
+
8
+ type Props = {
9
+ children: Snippet;
10
+ auth: AuthMethod | "guest";
11
+ peer: `wss://${string}` | `ws://${string}`;
12
+ storage?: "indexedDB" | "singleTabOPFS";
13
+ schema?: AccountClass<Acc>;
14
+ };
15
+
16
+ let {
17
+ children,
18
+ auth,
19
+ peer,
20
+ storage,
21
+ schema = Account as unknown as AccountClass<Acc>,
22
+ }: Props = $props();
23
+
24
+ const ctx = $state<JazzContext<Acc>>({ current: undefined });
25
+ setContext<JazzContext<Acc>>(JAZZ_CTX, ctx);
26
+ let sessionCount = $state(0);
27
+
28
+ $effect(() => {
29
+ schema;
30
+ auth;
31
+ peer;
32
+ storage;
33
+ sessionCount;
34
+ return untrack(() => {
35
+ if (!auth || !peer) return;
36
+
37
+ const promiseWithDoneCallback = createJazzBrowserContext<Acc>(
38
+ auth === "guest"
39
+ ? {
40
+ peer,
41
+ storage,
42
+ }
43
+ : {
44
+ AccountSchema: schema,
45
+ auth,
46
+ peer,
47
+ storage,
48
+ },
49
+ ).then((context) => {
50
+ ctx.current = {
51
+ ...context,
52
+ logOut: () => {
53
+ context.logOut();
54
+ ctx.current = undefined;
55
+ sessionCount = sessionCount + 1;
56
+ },
57
+ };
58
+ return context.done;
59
+ });
60
+ return () => {
61
+ void promiseWithDoneCallback.then((done) => done());
62
+ };
63
+ });
64
+ });
65
+ </script>
66
+
67
+ {#if ctx.current}
68
+ {@render children?.()}
69
+ {/if}
@@ -0,0 +1,26 @@
1
+ import type { AccountClass, AuthMethod } from "jazz-tools";
2
+ import { Account } from "jazz-tools";
3
+ import { type Snippet } from "svelte";
4
+ declare class __sveltets_Render<Acc extends Account> {
5
+ props(): {
6
+ children: Snippet;
7
+ auth: AuthMethod | "guest";
8
+ peer: `wss://${string}` | `ws://${string}`;
9
+ storage?: "indexedDB" | "singleTabOPFS";
10
+ schema?: AccountClass<Acc> | undefined;
11
+ };
12
+ events(): {};
13
+ slots(): {};
14
+ bindings(): "";
15
+ exports(): {};
16
+ }
17
+ interface $$IsomorphicComponent {
18
+ new <Acc extends Account>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Acc>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Acc>['props']>, ReturnType<__sveltets_Render<Acc>['events']>, ReturnType<__sveltets_Render<Acc>['slots']>> & {
19
+ $$bindings?: ReturnType<__sveltets_Render<Acc>['bindings']>;
20
+ } & ReturnType<__sveltets_Render<Acc>['exports']>;
21
+ <Acc extends Account>(internal: unknown, props: ReturnType<__sveltets_Render<Acc>['props']> & {}): ReturnType<__sveltets_Render<Acc>['exports']>;
22
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
23
+ }
24
+ declare const JazzProvider: $$IsomorphicComponent;
25
+ type JazzProvider<Acc extends Account> = InstanceType<typeof JazzProvider<Acc>>;
26
+ export default JazzProvider;
@@ -0,0 +1,24 @@
1
+ import { BrowserPasskeyAuth } from "jazz-browser";
2
+ export type PasskeyAuthState = ({
3
+ state: "uninitialized";
4
+ } | {
5
+ state: "loading";
6
+ } | {
7
+ state: "ready";
8
+ logIn: () => void;
9
+ signUp: (username: string) => void;
10
+ } | {
11
+ state: "signedIn";
12
+ logOut: () => void;
13
+ }) & {
14
+ errors: string[];
15
+ };
16
+ export type PasskeyAuth = {
17
+ current?: BrowserPasskeyAuth;
18
+ state: PasskeyAuthState;
19
+ };
20
+ /** @category Auth Providers */
21
+ export declare function usePasskeyAuth({ appName, appHostname, }: {
22
+ appName: string;
23
+ appHostname?: string;
24
+ }): PasskeyAuth;
@@ -0,0 +1,55 @@
1
+ import { BrowserPasskeyAuth } from "jazz-browser";
2
+ import { onMount } from "svelte";
3
+ /** @category Auth Providers */
4
+ export function usePasskeyAuth({ appName, appHostname, }) {
5
+ let instance = $state();
6
+ let state = $state({
7
+ state: "loading",
8
+ errors: [],
9
+ });
10
+ // Function to create a new auth instance
11
+ function createAuthInstance() {
12
+ instance = new BrowserPasskeyAuth({
13
+ onReady(next) {
14
+ state = {
15
+ state: "ready",
16
+ logIn: next.logIn,
17
+ signUp: next.signUp,
18
+ errors: [],
19
+ };
20
+ },
21
+ onSignedIn(next) {
22
+ state = {
23
+ state: "signedIn",
24
+ logOut: () => {
25
+ // First set state to loading
26
+ state = { state: "loading", errors: [] };
27
+ // Then trigger logout
28
+ next.logOut();
29
+ // Create new instance to trigger onReady
30
+ createAuthInstance();
31
+ },
32
+ errors: [],
33
+ };
34
+ },
35
+ onError(error) {
36
+ state = {
37
+ ...state,
38
+ errors: [...state.errors, error.toString()],
39
+ };
40
+ },
41
+ }, appName, appHostname);
42
+ }
43
+ // Initialize the auth instance on mount
44
+ onMount(() => {
45
+ createAuthInstance();
46
+ });
47
+ return {
48
+ get current() {
49
+ return instance;
50
+ },
51
+ get state() {
52
+ return state;
53
+ },
54
+ };
55
+ }
@@ -0,0 +1,65 @@
1
+ <script lang="ts">
2
+ import { type PasskeyAuthState } from 'jazz-svelte';
3
+
4
+ let { state: authState }: { state: PasskeyAuthState } = $props();
5
+
6
+ let name = $state('');
7
+
8
+ function signUp(e: Event) {
9
+ e.preventDefault();
10
+ if (!name.trim() || authState.state !== 'ready') return;
11
+ authState.signUp(name);
12
+ }
13
+
14
+ function logIn(e: Event) {
15
+ e.preventDefault();
16
+ e.stopPropagation();
17
+ if (authState.state !== 'ready') return;
18
+ authState.logIn();
19
+ }
20
+ </script>
21
+
22
+ <div style="max-width: 18rem; display: flex; flex-direction: column; gap: 2rem;">
23
+ {#if authState.state === 'loading'}
24
+ <div>Loading...</div>
25
+ {:else if authState.state === 'ready'}
26
+ {#if authState.errors?.length > 0}
27
+ <div style="color: red;">
28
+ {#each authState.errors as error}
29
+ <div>{error}</div>
30
+ {/each}
31
+ </div>
32
+ {/if}
33
+ <form onsubmit={signUp}>
34
+ <input type="text" placeholder="Display name" bind:value={name} autocomplete="name" />
35
+ <input type="submit" value="Sign up" />
36
+ </form>
37
+ <button onclick={logIn}> Log in with existing account </button>
38
+ {/if}
39
+ </div>
40
+
41
+ <style>
42
+ form {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: 0.5rem;
46
+ }
47
+
48
+ button,
49
+ input[type='submit'] {
50
+ background: #000;
51
+ color: #fff;
52
+ padding: 6px 12px;
53
+ border: none;
54
+ border-radius: 6px;
55
+ min-height: 38px;
56
+ cursor: pointer;
57
+ }
58
+
59
+ input[type='text'] {
60
+ border: 2px solid #000;
61
+ padding: 6px 12px;
62
+ border-radius: 6px;
63
+ min-height: 24px;
64
+ }
65
+ </style>
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ state: PasskeyAuthState;
3
+ };
4
+ import { type PasskeyAuthState } from 'jazz-svelte';
5
+ declare const PasskeyAuthBasicUi: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type PasskeyAuthBasicUi = ReturnType<typeof PasskeyAuthBasicUi>;
7
+ export default PasskeyAuthBasicUi;
@@ -0,0 +1,2 @@
1
+ export * from "./PasskeyAuth.svelte";
2
+ export { default as PasskeyAuthBasicUI } from "./PasskeyAuthBasicUI.svelte";
@@ -0,0 +1,2 @@
1
+ export * from "./PasskeyAuth.svelte";
2
+ export { default as PasskeyAuthBasicUI } from "./PasskeyAuthBasicUI.svelte";
@@ -0,0 +1,5 @@
1
+ import JazzProvider from "./JazzProvider.svelte";
2
+ export { createJazzApp } from "./jazz.svelte.js";
3
+ export { JazzProvider };
4
+ export { createInviteLink, parseInviteLink } from "jazz-browser";
5
+ export * from "./auth/index.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import JazzProvider from "./JazzProvider.svelte";
2
+ export { createJazzApp } from "./jazz.svelte.js";
3
+ export { JazzProvider };
4
+ export { createInviteLink, parseInviteLink } from "jazz-browser";
5
+ export * from "./auth/index.js";
@@ -0,0 +1,50 @@
1
+ import { type BrowserContext, type BrowserGuestContext } from "jazz-browser";
2
+ import type { AnonymousJazzAgent, CoValue, CoValueClass, DeeplyLoaded, DepthsIn, ID } from "jazz-tools";
3
+ import { Account } from "jazz-tools";
4
+ /**
5
+ * The key for the Jazz context.
6
+ */
7
+ export declare const JAZZ_CTX: {};
8
+ /**
9
+ * The Jazz context.
10
+ */
11
+ export type JazzContext<Acc extends Account> = {
12
+ current?: BrowserContext<Acc> | BrowserGuestContext;
13
+ };
14
+ /**
15
+ * Get the current Jazz context.
16
+ * @returns The current Jazz context.
17
+ */
18
+ export declare function getJazzContext<Acc extends Account>(): JazzContext<Acc>;
19
+ /**
20
+ * Create a Jazz app.
21
+ * @returns The Jazz app.
22
+ */
23
+ export declare function createJazzApp<Acc extends Account>(): {
24
+ useAccount: {
25
+ (): {
26
+ me: Acc | undefined;
27
+ logOut: () => void;
28
+ };
29
+ <D extends DepthsIn<Acc>>(depth: D): {
30
+ me: DeeplyLoaded<Acc, D> | undefined;
31
+ logOut: () => void;
32
+ };
33
+ };
34
+ useAccountOrGuest: {
35
+ (): {
36
+ me: Acc | AnonymousJazzAgent;
37
+ };
38
+ <D extends DepthsIn<Acc>>(depth: D): {
39
+ me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent;
40
+ };
41
+ };
42
+ useCoState: <V extends CoValue, D extends DepthsIn<V> = []>(Schema: CoValueClass<V>, id: ID<V> | undefined, depth?: D) => {
43
+ current?: DeeplyLoaded<V, D>;
44
+ };
45
+ useAcceptInvite: <V extends CoValue>({ invitedObjectSchema, onAccept, forValueHint, }: {
46
+ invitedObjectSchema: CoValueClass<V>;
47
+ onAccept: (projectID: ID<V>) => void;
48
+ forValueHint?: string;
49
+ }) => void;
50
+ };
@@ -0,0 +1,154 @@
1
+ import { consumeInviteLinkFromWindowLocation, } from "jazz-browser";
2
+ import { Account, createCoValueObservable } from "jazz-tools";
3
+ import { getContext, untrack } from "svelte";
4
+ /**
5
+ * The key for the Jazz context.
6
+ */
7
+ export const JAZZ_CTX = {};
8
+ /**
9
+ * Get the current Jazz context.
10
+ * @returns The current Jazz context.
11
+ */
12
+ export function getJazzContext() {
13
+ return getContext(JAZZ_CTX);
14
+ }
15
+ /**
16
+ * Create a Jazz app.
17
+ * @returns The Jazz app.
18
+ */
19
+ export function createJazzApp() {
20
+ /**
21
+ * Use the current account with a optional depth.
22
+ * @param depth - The depth.
23
+ * @returns The current account.
24
+ */
25
+ function useAccount(depth) {
26
+ const ctx = getJazzContext();
27
+ if (!ctx?.current) {
28
+ throw new Error("useAccount must be used within a JazzProvider");
29
+ }
30
+ if (!("me" in ctx.current)) {
31
+ throw new Error("useAccount can't be used in a JazzProvider with auth === 'guest' - consider using useAccountOrGuest()");
32
+ }
33
+ const me = useCoState(ctx.current.me.constructor, ctx.current.me.id, depth);
34
+ return {
35
+ get me() {
36
+ if (!ctx.current || !("me" in ctx.current))
37
+ return;
38
+ return depth === undefined ? me.current || ctx.current.me : me.current;
39
+ },
40
+ logOut() {
41
+ return ctx.current?.logOut();
42
+ },
43
+ };
44
+ }
45
+ /**
46
+ * Use the current account or guest with a optional depth.
47
+ * @param depth - The depth.
48
+ * @returns The current account or guest.
49
+ */
50
+ function useAccountOrGuest(depth) {
51
+ const ctx = getJazzContext();
52
+ if (!ctx?.current) {
53
+ throw new Error("useAccountOrGuest must be used within a JazzProvider");
54
+ }
55
+ const contextMe = "me" in ctx.current ? ctx.current.me : undefined;
56
+ const me = useCoState(contextMe?.constructor, contextMe?.id, depth);
57
+ // If the context has a me, return the account.
58
+ if ("me" in ctx.current) {
59
+ return {
60
+ get me() {
61
+ return depth === undefined
62
+ ? me.current || ctx.current?.me
63
+ : me.current;
64
+ },
65
+ };
66
+ }
67
+ // If the context has no me, return the guest.
68
+ else {
69
+ return {
70
+ get me() {
71
+ return ctx.current?.guest;
72
+ },
73
+ };
74
+ }
75
+ }
76
+ /**
77
+ * Use a CoValue with a optional depth.
78
+ * @param Schema - The CoValue schema.
79
+ * @param id - The CoValue id.
80
+ * @param depth - The depth.
81
+ * @returns The CoValue.
82
+ */
83
+ function useCoState(Schema, id, depth = []) {
84
+ const ctx = getJazzContext();
85
+ // Create state and a stable observable
86
+ let state = $state.raw(undefined);
87
+ const observable = $state.raw(createCoValueObservable());
88
+ // Effect to handle subscription
89
+ $effect(() => {
90
+ // Reset state when dependencies change
91
+ state = undefined;
92
+ // Get latest values
93
+ const currentCtx = ctx.current;
94
+ // Return early if no context or id, effectively cleaning up any previous subscription
95
+ if (!currentCtx || !id)
96
+ return;
97
+ // Setup subscription with current values
98
+ return observable.subscribe(Schema, id, "me" in currentCtx ? currentCtx.me : currentCtx.guest, depth, () => {
99
+ // Get current value from our stable observable
100
+ state = observable.getCurrentValue();
101
+ });
102
+ });
103
+ return {
104
+ get current() {
105
+ return state;
106
+ },
107
+ };
108
+ }
109
+ /**
110
+ * Use the accept invite hook.
111
+ * @param invitedObjectSchema - The invited object schema.
112
+ * @param onAccept - Function to call when the invite is accepted.
113
+ * @param forValueHint - Hint for the value.
114
+ * @returns The accept invite hook.
115
+ */
116
+ function useAcceptInvite({ invitedObjectSchema, onAccept, forValueHint, }) {
117
+ const ctx = getJazzContext();
118
+ const _onAccept = onAccept;
119
+ if (!ctx.current) {
120
+ throw new Error("useAcceptInvite must be used within a JazzProvider");
121
+ }
122
+ if (!("me" in ctx.current)) {
123
+ throw new Error("useAcceptInvite can't be used in a JazzProvider with auth === 'guest'.");
124
+ }
125
+ // Subscribe to the onAccept function.
126
+ $effect(() => {
127
+ _onAccept;
128
+ // Subscribe to the onAccept function.
129
+ untrack(() => {
130
+ // If there is no context, return.
131
+ if (!ctx.current)
132
+ return;
133
+ // Consume the invite link from the window location.
134
+ const result = consumeInviteLinkFromWindowLocation({
135
+ as: ctx.current.me,
136
+ invitedObjectSchema,
137
+ forValueHint,
138
+ });
139
+ // If the result is valid, call the onAccept function.
140
+ result
141
+ .then((result) => result && _onAccept(result?.valueID))
142
+ .catch((e) => {
143
+ console.error("Failed to accept invite", e);
144
+ });
145
+ });
146
+ });
147
+ }
148
+ return {
149
+ useAccount,
150
+ useAccountOrGuest,
151
+ useCoState,
152
+ useAcceptInvite,
153
+ };
154
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "jazz-svelte",
3
+ "version": "0.0.1",
4
+ "files": [
5
+ "dist",
6
+ "!dist/**/*.test.*",
7
+ "!dist/**/*.spec.*"
8
+ ],
9
+ "sideEffects": [
10
+ "**/*.css"
11
+ ],
12
+ "svelte": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "svelte": "./dist/index.js"
19
+ }
20
+ },
21
+ "peerDependencies": {
22
+ "svelte": "^5.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@sveltejs/adapter-auto": "^3.0.0",
26
+ "@sveltejs/kit": "^2.0.0",
27
+ "@sveltejs/package": "^2.0.0",
28
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
29
+ "@types/eslint": "^9.6.0",
30
+ "eslint": "^9.7.0",
31
+ "eslint-config-prettier": "^9.1.0",
32
+ "eslint-plugin-svelte": "^2.36.0",
33
+ "globals": "^15.0.0",
34
+ "prettier": "^3.3.2",
35
+ "prettier-plugin-svelte": "^3.2.6",
36
+ "publint": "^0.2.0",
37
+ "svelte": "^5.0.0",
38
+ "svelte-check": "^4.0.0",
39
+ "typescript": "^5.0.0",
40
+ "typescript-eslint": "^8.0.0",
41
+ "vite": "^5.0.11"
42
+ },
43
+ "dependencies": {
44
+ "jazz-browser": "0.8.34",
45
+ "jazz-tools": "0.8.34"
46
+ },
47
+ "scripts": {
48
+ "dev": "vite dev",
49
+ "build": "vite build && npm run package",
50
+ "preview": "vite preview",
51
+ "package": "svelte-kit sync && svelte-package && publint",
52
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
53
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
54
+ "format": "prettier --write .",
55
+ "lint": "prettier --check . && eslint ."
56
+ }
57
+ }