Skip to content

Commit

Permalink
Merge branch 'pr/matthewgapp/37'
Browse files Browse the repository at this point in the history
  • Loading branch information
carlsverre committed Jan 8, 2024
2 parents 4178a18 + 65c103d commit 518bae5
Show file tree
Hide file tree
Showing 18 changed files with 1,180 additions and 27 deletions.
3 changes: 3 additions & 0 deletions examples/guestbook-solid-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Solid JS + TypeScript + Vite + SQLSync

This template provides a minimal setup to get SQLSync working with Solid JS and Vite.
15 changes: 15 additions & 0 deletions examples/guestbook-solid-js/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SQLSync + Vite + Solid JS + TS</title>
</head>

<body>
<div id="root">if nothing renders, open the console</div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
24 changes: 24 additions & 0 deletions examples/guestbook-solid-js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "guestbook-react",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@orbitinghail/sqlsync-solid-js": "workspace:*",
"@orbitinghail/sqlsync-worker": "workspace:*",
"uuid": "^9.0.1",
"solid-js": "^1.8.7"
},
"devDependencies": {
"@types/uuid": "^9.0.6",
"vite-plugin-solid": "^2.7.0",
"typescript": "^5.2.2",
"vite": "^4.5.0",
"vitest": "^0.34.6"
}
}
29 changes: 29 additions & 0 deletions examples/guestbook-solid-js/src/doctype.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createDocHooks } from "@orbitinghail/sqlsync-solid-js";
import { DocType, serializeMutationAsJSON } from "@orbitinghail/sqlsync-worker";

const REDUCER_URL = new URL(
"../../../target/wasm32-unknown-unknown/release/reducer_guestbook.wasm",
import.meta.url,
);

// Must match the Mutation type in the Rust Reducer code
export type Mutation =
| {
tag: "InitSchema";
}
| {
tag: "AddMessage";
id: string;
msg: string;
}
| {
tag: "DeleteMessage";
id: string;
};

export const TaskDocType: DocType<Mutation> = {
reducerUrl: REDUCER_URL,
serializeMutation: serializeMutationAsJSON,
};

export const { useMutate, useQuery, useSetConnectionEnabled } = createDocHooks(() => TaskDocType);
114 changes: 114 additions & 0 deletions examples/guestbook-solid-js/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { SQLSyncProvider } from "@orbitinghail/sqlsync-solid-js";
import { journalIdFromString, sql } from "@orbitinghail/sqlsync-worker";

// this example uses the uuid library (`npm install uuid`)
import { JSX } from "solid-js/jsx-runtime";
import { v4 as uuidv4 } from "uuid";

// You'll need to configure your build system to make these entrypoints
// available as urls. Vite does this automatically via the `?url` and `?worker&url` suffix.
import sqlSyncWasmUrl from "@orbitinghail/sqlsync-worker/sqlsync.wasm?url";
import workerUrl from "@orbitinghail/sqlsync-worker/worker.ts?worker&url";

import { createEffect, createSignal } from "solid-js";
import { For, render } from "solid-js/web";
import { useMutate, useQuery } from "./doctype";

// Create a DOC_ID to use, each DOC_ID will correspond to a different SQLite
// database. We use a static doc id so we can play with cross-tab sync.
const DOC_ID = journalIdFromString("VM7fC4gKxa52pbdtrgd9G9");

// Use SQLSync hooks in your app
export function App() {
// we will use the standard useState hook to handle the message input box
const [msg, setMsg] = createSignal("");

// create a mutate function for our document
const mutate = useMutate(DOC_ID);

// initialize the schema; eventually this will be handled by SQLSync automatically
createEffect(() => {
mutate({ tag: "InitSchema" }).catch((err) => {
console.error("Failed to init schema", err);
});
});

// create a callback which knows how to trigger the add message mutation
const handleSubmit: JSX.EventHandler<HTMLFormElement, SubmitEvent> = (e) => {
// Prevent the browser from reloading the page
e.preventDefault();

// create a unique message id
const id = crypto.randomUUID ? crypto.randomUUID() : uuidv4();

// don't add empty messages
if (msg().trim() !== "") {
mutate({ tag: "AddMessage", id, msg: msg() }).catch((err) => {
console.error("Failed to add message", err);
});
// clear the message
setMsg("");
}
};

// create a callback to delete a message
const handleDelete = (id: string) => {
mutate({ tag: "DeleteMessage", id }).catch((err) => {
console.error("Failed to delete message", err);
});
};

// finally, query SQLSync for all the messages, sorted by created_at
const queryState = useQuery<{ id: string; msg: string }>(
() => DOC_ID,
() => sql`
select id, msg from messages
order by created_at
`,
);

const rows = () => queryState().rows ?? [];

return (
<div>
<h1>Guestbook:</h1>
<ul>
<For each={rows()}>
{(row) => {
return (
<li>
{row.msg}
<button
type="button"
onClick={() => handleDelete(row.id)}
style={{ "margin-left": "40px" }}
>
remove msg
</button>
</li>
);
}}
</For>
</ul>
<h3>Leave a message:</h3>
<form onSubmit={handleSubmit}>
<label>
Msg:
<input type="text" name="msg" value={msg()} onChange={(e) => setMsg(e.target.value)} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}

// Configure the SQLSync provider near the top of the React tree
render(
() => (
<SQLSyncProvider wasmUrl={sqlSyncWasmUrl} workerUrl={workerUrl}>
<App />
</SQLSyncProvider>
),
// biome-ignore lint/style/noNonNullAssertion: we know this element exists
document.getElementById("root")!,
);
27 changes: 27 additions & 0 deletions examples/guestbook-solid-js/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "ES2021.WeakRef", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"types": ["vite/client"],

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
10 changes: 10 additions & 0 deletions examples/guestbook-solid-js/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
7 changes: 7 additions & 0 deletions examples/guestbook-solid-js/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from "vite";
import solid from "vite-plugin-solid";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [solid()],
});
6 changes: 6 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ wasm-examples-reducer-guestbook:
example-guestbook-react: wasm-examples-reducer-guestbook
cd examples/guestbook-react && pnpm dev

example-guestbook-solid-js: wasm-examples-reducer-guestbook
cd examples/guestbook-solid-js && pnpm dev

test-end-to-end-local rng_seed="": wasm-task-reducer
RUST_BACKTRACE=1 cargo run --example end-to-end-local {{rng_seed}}

Expand All @@ -64,6 +67,9 @@ node_modules:
package-sqlsync-react:
cd lib/sqlsync-react && pnpm build

package-sqlsync-solid-js:
cd lib/sqlsync-solid-js && pnpm build

package-sqlsync-worker target='release':
#!/usr/bin/env bash
if [[ '{{target}}' = 'release' ]]; then
Expand Down
3 changes: 1 addition & 2 deletions lib/sqlsync-react/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"target": "esnext",
"module": "esnext",
"moduleResolution": "Bundler",
"baseUrl": ".",
"lib": ["ES6", "DOM", "ES2021.WeakRef"],
"lib": ["ES6", "DOM", "DOM.Iterable", "ES2021.WeakRef"],

/* Compiling */
"allowJs": true,
Expand Down
65 changes: 65 additions & 0 deletions lib/sqlsync-solid-js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "@orbitinghail/sqlsync-solid-js",
"version": "0.2.0",
"description": "SQLSync is a collaborative offline-first wrapper around SQLite. It is designed to synchronize web application state between users, devices, and the edge.",
"homepage": "https://sqlsync.dev",
"license": "Apache-2.0",
"keywords": [
"sqlsync",
"sql",
"database",
"sqlite",
"offline-first",
"local-first",
"solid-js"
],
"repository": {
"type": "git",
"url": "https://github.com/orbitinghail/sqlsync"
},
"files": [
"dist",
"src"
],
"type": "module",
"types": "./src/index.ts",
"main": "./src/index.ts",
"exports": {
".": {
"import": "./src/index.ts",
"default": "./src/index.ts",
"types": "./src/index.ts"
}
},
"publishConfig": {
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"default": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}
},
"scripts": {
"build": "rollup --config"
},
"devDependencies": {
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.5",
"@types/node": "^20.8.8",
"babel-preset-solid": "^1.8.8",
"@orbitinghail/sqlsync-worker": "workspace:^",
"rollup": "^3.29.4",
"typescript": "^5.2.2"
},
"dependencies": {
"fast-equals": "^5.0.1"
},
"peerDependencies": {
"solid-js": "^1.8.7",
"@orbitinghail/sqlsync-worker": "workspace:^"
}
}
23 changes: 23 additions & 0 deletions lib/sqlsync-solid-js/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import babel from "@rollup/plugin-babel";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";

export default {
input: "src/index.ts",
output: {
dir: "dist",
format: "es",
sourcemap: true,
},
external: ["solid-js", "@orbitinghail/sqlsync-worker"],
plugins: [
typescript(),
nodeResolve(),
babel({
extensions: [".ts", ".tsx"],
babelHelpers: "bundled",
presets: ["solid", "@babel/preset-typescript"],
exclude: [/node_modules\//],
}),
],
};
34 changes: 34 additions & 0 deletions lib/sqlsync-solid-js/src/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SQLSync } from "@orbitinghail/sqlsync-worker";
import { ParentComponent, createContext, createSignal, onCleanup } from "solid-js";

export const SQLSyncContext = createContext<[() => SQLSync | null, (sqlSync: SQLSync) => void]>([
() => null,
() => {},
]);

interface Props {
workerUrl: string | URL;
wasmUrl: string | URL;
coordinatorUrl?: string | URL;
}

export const createSqlSync = (props: Props): SQLSync => {
return new SQLSync(props.workerUrl, props.wasmUrl, props.coordinatorUrl);
};

export const SQLSyncProvider: ParentComponent<Props> = (props) => {
const [sqlSync, setSQLSync] = createSignal<SQLSync>(createSqlSync(props));

onCleanup(() => {
const s = sqlSync();
if (s) {
s.close();
}
});

return (
<SQLSyncContext.Provider value={[sqlSync, setSQLSync]}>
{props.children}
</SQLSyncContext.Provider>
);
};
Loading

0 comments on commit 518bae5

Please sign in to comment.