Compare commits
9 Commits
docs/loadi
...
jazz-react
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dfd74d7f5 | ||
|
|
5fbd598cfe | ||
|
|
d54b53737d | ||
|
|
0f0cd08c36 | ||
|
|
4bb3834333 | ||
|
|
b9007dd1b5 | ||
|
|
ee1e5b06e4 | ||
|
|
fae290c4cf | ||
|
|
381d68019f |
81
.github/workflows/build-and-deploy.yaml
vendored
Normal file
81
.github/workflows/build-and-deploy.yaml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Build and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: yarn.lock
|
||||
- name: Nuke Workspace
|
||||
run: |
|
||||
rm package.json yarn.lock;
|
||||
- name: Yarn Build
|
||||
run: |
|
||||
yarn install --frozen-lockfile;
|
||||
yarn build;
|
||||
working-directory: ./examples/todo
|
||||
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.11
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: docker-layer-caching-${{ github.workflow }}-{hash}
|
||||
restore-keys: |
|
||||
docker-layer-caching-${{ github.workflow }}-
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: gardencmp
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker Build & Push
|
||||
run: |
|
||||
export DOCKER_TAG=ghcr.io/gardencmp/jazz-example-todo:${{github.head_ref || github.ref_name}}-${{github.sha}}-$(date +%s) ;
|
||||
docker build . --file Dockerfile --tag $DOCKER_TAG;
|
||||
docker push $DOCKER_TAG;
|
||||
echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV
|
||||
working-directory: ./examples/todo
|
||||
|
||||
- uses: gacts/install-nomad@v1
|
||||
- name: Tailscale
|
||||
uses: tailscale/github-action@v1
|
||||
with:
|
||||
authkey: ${{ secrets.TAILSCALE_AUTHKEY }}
|
||||
|
||||
- name: Deploy on Nomad
|
||||
run: |
|
||||
if [ "${{github.ref_name}}" == "main" ]; then
|
||||
export BRANCH_SUFFIX="";
|
||||
export BRANCH_SUBDOMAIN="";
|
||||
else
|
||||
export BRANCH_SUFFIX=-${{github.head_ref || github.ref_name}};
|
||||
export BRANCH_SUBDOMAIN=${{github.head_ref || github.ref_name}}.;
|
||||
fi
|
||||
|
||||
export DOCKER_USER=gardencmp;
|
||||
export DOCKER_PASSWORD=${{ secrets.DOCKER_PULL_PAT }};
|
||||
export DOCKER_TAG=${{ env.DOCKER_TAG }};
|
||||
|
||||
for region in ${{ vars.DEPLOY_REGIONS }}
|
||||
do
|
||||
export REGION=$region;
|
||||
envsubst '${DOCKER_USER} ${DOCKER_PASSWORD} ${DOCKER_TAG} ${BRANCH_SUFFIX} ${BRANCH_SUBDOMAIN} ${REGION}' < job-template.nomad > job-instance.nomad;
|
||||
cat job-instance.nomad;
|
||||
NOMAD_ADDR='${{ secrets.NOMAD_ADDR }}' nomad job run job-instance.nomad;
|
||||
done
|
||||
working-directory: ./examples/todo
|
||||
6
examples/todo/Dockerfile
Normal file
6
examples/todo/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM caddy:2.7.3-alpine
|
||||
LABEL org.opencontainers.image.source="https://github.com/gardencmp/jazz"
|
||||
|
||||
COPY ./dist /usr/share/caddy/
|
||||
|
||||
RUN caddy
|
||||
54
examples/todo/job-template.nomad
Normal file
54
examples/todo/job-template.nomad
Normal file
@@ -0,0 +1,54 @@
|
||||
job "example-todo$BRANCH_SUFFIX" {
|
||||
region = "$REGION"
|
||||
datacenters = ["$REGION"]
|
||||
|
||||
group "static" {
|
||||
// count = 3
|
||||
|
||||
network {
|
||||
port "http" {
|
||||
to = 80
|
||||
}
|
||||
}
|
||||
|
||||
constraint {
|
||||
attribute = "${node.class}"
|
||||
operator = "="
|
||||
value = "edge"
|
||||
}
|
||||
|
||||
// spread {
|
||||
// attribute = "${node.datacenter}"
|
||||
// weight = 100
|
||||
// }
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "$DOCKER_TAG"
|
||||
ports = ["http"]
|
||||
|
||||
auth = {
|
||||
username = "$DOCKER_USER"
|
||||
password = "$DOCKER_PASSWORD"
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
tags = ["public"]
|
||||
meta {
|
||||
public_name = "${BRANCH_SUBDOMAIN}example-todo"
|
||||
}
|
||||
port = "http"
|
||||
provider = "consul"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 50 # MHz
|
||||
memory = 50 # MB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# deploy bump 4
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.5",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -14,8 +14,8 @@
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "^0.0.9",
|
||||
"jazz-react-auth-local": "^0.0.6",
|
||||
"jazz-react": "^0.0.11",
|
||||
"jazz-react-auth-local": "^0.0.8",
|
||||
"lucide-react": "^0.265.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -26,7 +26,7 @@ function App() {
|
||||
|
||||
const createList = () => {
|
||||
const listTeam = localNode.createTeam();
|
||||
const list = listTeam.createMap<TodoListContent, null>();
|
||||
const list = listTeam.createMap<TodoListContent>();
|
||||
|
||||
list.edit((list) => {
|
||||
list.set("title", "My Todo List");
|
||||
@@ -47,7 +47,7 @@ function App() {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full items-center justify-center gap-10">
|
||||
<div className="flex flex-col h-full items-center justify-start gap-10 pt-10 md:pt-[30vh] pb-10">
|
||||
{listId && <TodoList listId={listId} />}
|
||||
<Button onClick={createList}>Create New List</Button>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@ export function TodoList({ listId }: { listId: CoID<TodoList> }) {
|
||||
|
||||
const createTodo = (text: string) => {
|
||||
if (!list) return;
|
||||
let task = list.coValue.getTeam().createMap<TaskContent, {}>();
|
||||
let task = list.coValue.getTeam().createMap<TaskContent>();
|
||||
|
||||
task = task.edit((task) => {
|
||||
task.set("text", text);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import { Input } from "./components/ui/input.tsx";
|
||||
import { Button } from "./components/ui/button.tsx";
|
||||
import { AuthComponent } from "jazz-react";
|
||||
|
||||
@@ -7,7 +7,7 @@ import { LocalAuth } from "./LocalAuth.tsx";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<WithJazz auth={LocalAuth}>
|
||||
<WithJazz auth={LocalAuth} syncAddress={new URLSearchParams(window.location.search).get("sync") || undefined}>
|
||||
<App />
|
||||
</WithJazz>
|
||||
</React.StrictMode>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
"types": "src/index.ts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.0.17",
|
||||
"version": "0.0.18",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { CoValue, newRandomSessionID } from "./coValue.js";
|
||||
import { LocalNode } from "./node.js";
|
||||
import { CoMap } from "./contentTypes/coMap.js";
|
||||
import { agentSecretFromBytes, agentSecretToBytes } from "./crypto.js";
|
||||
import {
|
||||
agentSecretFromBytes,
|
||||
agentSecretToBytes,
|
||||
getAgentID,
|
||||
newRandomAgentSecret,
|
||||
} from "./crypto.js";
|
||||
import { connectedPeers } from "./streamUtils.js";
|
||||
import { AnonymousControlledAccount, ControlledAccount } from "./account.js";
|
||||
|
||||
import type { SessionID } from "./ids.js";
|
||||
import type { SessionID, AgentID } from "./ids.js";
|
||||
import type { CoID, ContentType } from "./contentType.js";
|
||||
import type { JsonValue } from "./jsonValue.js";
|
||||
import type { SyncMessage } from "./sync.js";
|
||||
@@ -12,14 +18,23 @@ import type { AgentSecret } from "./crypto.js";
|
||||
|
||||
type Value = JsonValue | ContentType;
|
||||
|
||||
const internals = {
|
||||
export const cojsonInternals = {
|
||||
agentSecretFromBytes,
|
||||
agentSecretToBytes,
|
||||
newRandomSessionID,
|
||||
connectedPeers
|
||||
newRandomAgentSecret,
|
||||
connectedPeers,
|
||||
getAgentID,
|
||||
};
|
||||
|
||||
export { LocalNode, CoValue, CoMap, internals };
|
||||
export {
|
||||
LocalNode,
|
||||
CoValue,
|
||||
CoMap,
|
||||
cojsonInternals as internals,
|
||||
AnonymousControlledAccount,
|
||||
ControlledAccount,
|
||||
};
|
||||
|
||||
export type {
|
||||
Value,
|
||||
@@ -29,4 +44,17 @@ export type {
|
||||
AgentSecret,
|
||||
SessionID,
|
||||
SyncMessage,
|
||||
AgentID,
|
||||
};
|
||||
|
||||
export namespace CojsonInternalTypes {
|
||||
export type CoValueKnownState = import("./sync.js").CoValueKnownState;
|
||||
export type DoneMessage = import("./sync.js").DoneMessage;
|
||||
export type KnownStateMessage = import("./sync.js").KnownStateMessage;
|
||||
export type LoadMessage = import("./sync.js").LoadMessage;
|
||||
export type NewContentMessage = import("./sync.js").NewContentMessage;
|
||||
export type CoValueHeader = import("./coValue.js").CoValueHeader;
|
||||
export type Transaction = import("./coValue.js").Transaction;
|
||||
export type Signature = import("./crypto.js").Signature;
|
||||
export type RawCoID = import("./ids.js").RawCoID;
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ export class Team {
|
||||
this.rotateReadKey();
|
||||
}
|
||||
|
||||
createMap<M extends { [key: string]: JsonValue }, Meta extends JsonObject | null>(
|
||||
createMap<M extends { [key: string]: JsonValue }, Meta extends JsonObject | null = null>(
|
||||
meta?: Meta
|
||||
): CoMap<M, Meta> {
|
||||
return this.node
|
||||
|
||||
@@ -100,14 +100,14 @@ export function newStreamPair<T>(): [ReadableStream<T>, WritableStream<T>] {
|
||||
);
|
||||
},
|
||||
|
||||
cancel(reason) {
|
||||
cancel(_reason) {
|
||||
console.log("Manually closing reader");
|
||||
readerClosed = true;
|
||||
},
|
||||
});
|
||||
|
||||
const writable = new WritableStream<T>({
|
||||
write(chunk, controller) {
|
||||
write(chunk) {
|
||||
if (readerClosed) {
|
||||
console.log("Reader closed, not writing chunk", chunk);
|
||||
throw new Error("Reader closed, not writing chunk");
|
||||
@@ -118,7 +118,7 @@ export function newStreamPair<T>(): [ReadableStream<T>, WritableStream<T>] {
|
||||
setTimeout(() => resolveNextItemReady());
|
||||
}
|
||||
},
|
||||
abort(reason) {
|
||||
abort(_reason) {
|
||||
console.log("Manually closing writer");
|
||||
writerClosed = true;
|
||||
resolveNextItemReady();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Hash, Signature } from "./crypto.js";
|
||||
import { Signature } from "./crypto.js";
|
||||
import { CoValueHeader, Transaction } from "./coValue.js";
|
||||
import { CoValue } from "./coValue.js";
|
||||
import { LocalNode } from "./node.js";
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:require-extensions/recommended",
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint", "require-extensions"],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
root: true,
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
||||
],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
},
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
2
packages/jazz-react-auth-local/.npmignore
Normal file
2
packages/jazz-react-auth-local/.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage
|
||||
node_modules
|
||||
@@ -1,17 +1,22 @@
|
||||
{
|
||||
"name": "jazz-react-auth-local",
|
||||
"version": "0.0.6",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jazz-react": "^0.0.9",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2"
|
||||
}
|
||||
"name": "jazz-react-auth-local",
|
||||
"version": "0.0.8",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jazz-react": "^0.0.11",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"build": "npm run lint && rm -rf ./dist && tsc --declaration --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { newRandomAgentSecret, AgentSecret, agentSecretToBytes, agentSecretFromBytes} from "cojson/src/crypto";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { cojsonInternals, AgentSecret } from "cojson";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
const [displayName, setDisplayName] = useState<string>("");
|
||||
@@ -12,8 +12,8 @@ export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
}, [onCredential]);
|
||||
|
||||
const signUp = useCallback(() => {
|
||||
(async function () {
|
||||
const credential = newRandomAgentSecret();
|
||||
void (async function () {
|
||||
const credential = cojsonInternals.newRandomAgentSecret();
|
||||
|
||||
console.log(credential);
|
||||
|
||||
@@ -26,7 +26,7 @@ export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
id: window.location.hostname,
|
||||
},
|
||||
user: {
|
||||
id: agentSecretToBytes(credential),
|
||||
id: cojsonInternals.agentSecretToBytes(credential),
|
||||
name: displayName,
|
||||
displayName: displayName,
|
||||
},
|
||||
@@ -42,7 +42,7 @@ export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
console.log(
|
||||
webAuthNCredential,
|
||||
credential,
|
||||
agentSecretToBytes(credential)
|
||||
cojsonInternals.agentSecretToBytes(credential)
|
||||
);
|
||||
|
||||
sessionStorage.credential = JSON.stringify(credential);
|
||||
@@ -51,7 +51,7 @@ export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
}, [displayName]);
|
||||
|
||||
const signIn = useCallback(() => {
|
||||
(async function () {
|
||||
void (async function () {
|
||||
const webAuthNCredential = await navigator.credentials.get({
|
||||
publicKey: {
|
||||
challenge: Uint8Array.from([0, 1, 2]),
|
||||
@@ -62,11 +62,19 @@ export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
},
|
||||
});
|
||||
|
||||
if (!webAuthNCredential) {
|
||||
throw new Error("Couldn't log in");
|
||||
}
|
||||
|
||||
const userIdBytes = new Uint8Array(
|
||||
(webAuthNCredential as any).response.userHandle
|
||||
(
|
||||
webAuthNCredential as unknown as {
|
||||
response: { userHandle: ArrayBuffer };
|
||||
}
|
||||
).response.userHandle
|
||||
);
|
||||
const credential =
|
||||
agentSecretFromBytes(userIdBytes);
|
||||
cojsonInternals.agentSecretFromBytes(userIdBytes);
|
||||
|
||||
if (!credential) {
|
||||
throw new Error("Invalid credential");
|
||||
@@ -78,4 +86,4 @@ export function useLocalAuth(onCredential: (credentials: AgentSecret) => void) {
|
||||
}, []);
|
||||
|
||||
return { displayName, setDisplayName, signUp, signIn };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,12 @@
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"target": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
root: true,
|
||||
rules: {
|
||||
@@ -14,5 +14,4 @@ module.exports = {
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
},
|
||||
|
||||
};
|
||||
};
|
||||
2
packages/jazz-react/.npmignore
Normal file
2
packages/jazz-react/.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage
|
||||
node_modules
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "jazz-react",
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.11",
|
||||
"main": "src/index.tsx",
|
||||
"types": "src/index.tsx",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "^0.0.17",
|
||||
"cojson": "^0.0.18",
|
||||
"jazz-storage-indexeddb": "^0.0.5",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -13,5 +14,10 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint src/**/*.tsx",
|
||||
"build": "npm run lint && rm -rf ./dist && tsc --declaration --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import {
|
||||
LocalNode,
|
||||
internals as cojsonInternals,
|
||||
cojsonInternals,
|
||||
SessionID,
|
||||
ContentType,
|
||||
SyncMessage,
|
||||
AgentSecret,
|
||||
CoID,
|
||||
AnonymousControlledAccount,
|
||||
AgentID
|
||||
} from "cojson";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { ReadableStream, WritableStream } from "isomorphic-streams";
|
||||
import { CoID } from "cojson";
|
||||
import { AgentID } from "cojson/src/ids";
|
||||
import { getAgentID } from "cojson/src/crypto";
|
||||
import { AnonymousControlledAccount } from "cojson/src/account";
|
||||
import { IDBStorage } from "jazz-storage-indexeddb";
|
||||
|
||||
type JazzContext = {
|
||||
@@ -37,10 +36,10 @@ export function WithJazz({
|
||||
const sessionDone = useRef<() => void>();
|
||||
|
||||
const onCredential = useCallback((credential: AgentSecret) => {
|
||||
const agentID = getAgentID(credential);
|
||||
const agentID = cojsonInternals.getAgentID(credential);
|
||||
const sessionHandle = getSessionFor(agentID);
|
||||
|
||||
sessionHandle.session.then((sessionID) =>
|
||||
void sessionHandle.session.then((sessionID) =>
|
||||
setNode(
|
||||
new LocalNode(
|
||||
new AnonymousControlledAccount(credential),
|
||||
@@ -60,12 +59,12 @@ export function WithJazz({
|
||||
|
||||
useEffect(() => {
|
||||
if (node) {
|
||||
IDBStorage.connectTo(node, { trace: true });
|
||||
void IDBStorage.connectTo(node, { trace: true });
|
||||
|
||||
let shouldTryToReconnect = true;
|
||||
let ws: WebSocket | undefined;
|
||||
|
||||
(async function websocketReconnectLoop() {
|
||||
void (async function websocketReconnectLoop() {
|
||||
while (shouldTryToReconnect) {
|
||||
ws = new WebSocket(syncAddress);
|
||||
|
||||
@@ -134,7 +133,7 @@ function getSessionFor(agentID: AgentID): SessionHandle {
|
||||
resolveSession = resolve;
|
||||
});
|
||||
|
||||
(async function () {
|
||||
void (async function () {
|
||||
for (let idx = 0; idx < 100; idx++) {
|
||||
// To work better around StrictMode
|
||||
for (let retry = 0; retry < 2; retry++) {
|
||||
@@ -209,6 +208,8 @@ export function useTelepathicState<T extends ContentType>(id: CoID<T>) {
|
||||
);
|
||||
setState(newState as T);
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.log("Failed to load", id, e);
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"target": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "react",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true,
|
||||
},
|
||||
|
||||
@@ -6,13 +6,12 @@ module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
root: true,
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
// "@typescript-eslint/no-floating-promises": "error",
|
||||
},
|
||||
|
||||
};
|
||||
};
|
||||
2
packages/jazz-storage-indexeddb/.npmignore
Normal file
2
packages/jazz-storage-indexeddb/.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage
|
||||
node_modules
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "jazz-storage-indexeddb",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "^0.0.17",
|
||||
"cojson": "^0.0.18",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -14,6 +14,9 @@
|
||||
"webdriverio": "^8.15.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest --browser chrome"
|
||||
"test": "vitest --browser chrome",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"build": "npm run lint && rm -rf ./dist && tsc --declaration --sourceMap --outDir dist",
|
||||
"prepublishOnly": "npm run build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ test.skip("Should be able to initialize and load from empty DB", async () => {
|
||||
|
||||
console.log("yay!");
|
||||
|
||||
const team = node.createTeam();
|
||||
const _team = node.createTeam();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
import {
|
||||
LocalNode,
|
||||
internals as cojsonInternals,
|
||||
SessionID,
|
||||
ContentType,
|
||||
SyncMessage,
|
||||
JsonValue,
|
||||
} from "cojson";
|
||||
import { CoValueHeader, Transaction } from "cojson/src/coValue";
|
||||
import { Signature } from "cojson/src/crypto";
|
||||
import { RawCoID } from "cojson/src/ids";
|
||||
import {
|
||||
CoValueKnownState,
|
||||
DoneMessage,
|
||||
KnownStateMessage,
|
||||
LoadMessage,
|
||||
NewContentMessage,
|
||||
} from "cojson/src/sync";
|
||||
import { LocalNode, cojsonInternals, SessionID, SyncMessage } from "cojson";
|
||||
import { CojsonInternalTypes } from "cojson";
|
||||
import {
|
||||
ReadableStream,
|
||||
WritableStream,
|
||||
@@ -24,8 +8,8 @@ import {
|
||||
} from "isomorphic-streams";
|
||||
|
||||
type CoValueRow = {
|
||||
id: RawCoID;
|
||||
header: CoValueHeader;
|
||||
id: CojsonInternalTypes.RawCoID;
|
||||
header: CojsonInternalTypes.CoValueHeader;
|
||||
};
|
||||
|
||||
type StoredCoValueRow = CoValueRow & { rowID: number };
|
||||
@@ -34,7 +18,7 @@ type SessionRow = {
|
||||
coValue: number;
|
||||
sessionID: SessionID;
|
||||
lastIdx: number;
|
||||
lastSignature: Signature;
|
||||
lastSignature: CojsonInternalTypes.Signature;
|
||||
};
|
||||
|
||||
type StoredSessionRow = SessionRow & { rowID: number };
|
||||
@@ -42,7 +26,7 @@ type StoredSessionRow = SessionRow & { rowID: number };
|
||||
type TransactionRow = {
|
||||
ses: number;
|
||||
idx: number;
|
||||
tx: Transaction;
|
||||
tx: CojsonInternalTypes.Transaction;
|
||||
};
|
||||
|
||||
export class IDBStorage {
|
||||
@@ -60,13 +44,14 @@ export class IDBStorage {
|
||||
this.toLocalNode = toLocalNode.getWriter();
|
||||
|
||||
(async () => {
|
||||
while (true) {
|
||||
const { done, value } = await this.fromLocalNode.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
let done = false;
|
||||
while (!done) {
|
||||
const result = await this.fromLocalNode.read();
|
||||
done = result.done;
|
||||
|
||||
this.handleSyncMessage(value);
|
||||
if (result.value) {
|
||||
this.handleSyncMessage(result.value);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
@@ -159,7 +144,7 @@ export class IDBStorage {
|
||||
}
|
||||
|
||||
async sendNewContentAfter(
|
||||
theirKnown: CoValueKnownState,
|
||||
theirKnown: CojsonInternalTypes.CoValueKnownState,
|
||||
{
|
||||
coValues,
|
||||
sessions,
|
||||
@@ -169,7 +154,7 @@ export class IDBStorage {
|
||||
sessions: IDBObjectStore;
|
||||
transactions: IDBObjectStore;
|
||||
},
|
||||
asDependencyOf?: RawCoID
|
||||
asDependencyOf?: CojsonInternalTypes.RawCoID
|
||||
) {
|
||||
const coValueRow = await promised<StoredCoValueRow | undefined>(
|
||||
coValues.index("coValuesById").get(theirKnown.id)
|
||||
@@ -181,13 +166,13 @@ export class IDBStorage {
|
||||
)
|
||||
: [];
|
||||
|
||||
const ourKnown: CoValueKnownState = {
|
||||
const ourKnown: CojsonInternalTypes.CoValueKnownState = {
|
||||
id: theirKnown.id,
|
||||
header: !!coValueRow,
|
||||
sessions: {},
|
||||
};
|
||||
|
||||
const newContent: NewContentMessage = {
|
||||
const newContent: CojsonInternalTypes.NewContentMessage = {
|
||||
action: "content",
|
||||
id: theirKnown.id,
|
||||
header: theirKnown.header ? undefined : coValueRow?.header,
|
||||
@@ -237,7 +222,7 @@ export class IDBStorage {
|
||||
change.key
|
||||
)
|
||||
.filter(
|
||||
(key): key is RawCoID =>
|
||||
(key): key is CojsonInternalTypes.RawCoID =>
|
||||
typeof key === "string" &&
|
||||
key.startsWith("co_")
|
||||
);
|
||||
@@ -258,7 +243,7 @@ export class IDBStorage {
|
||||
await this.toLocalNode.write({
|
||||
action: "known",
|
||||
...ourKnown,
|
||||
asDependencyOf
|
||||
asDependencyOf,
|
||||
});
|
||||
|
||||
if (newContent.header || Object.keys(newContent.new).length > 0) {
|
||||
@@ -266,11 +251,11 @@ export class IDBStorage {
|
||||
}
|
||||
}
|
||||
|
||||
handleLoad(msg: LoadMessage) {
|
||||
handleLoad(msg: CojsonInternalTypes.LoadMessage) {
|
||||
return this.sendNewContentAfter(msg, this.inTransaction("readonly"));
|
||||
}
|
||||
|
||||
async handleContent(msg: NewContentMessage) {
|
||||
async handleContent(msg: CojsonInternalTypes.NewContentMessage) {
|
||||
const { coValues, sessions, transactions } =
|
||||
this.inTransaction("readwrite");
|
||||
|
||||
@@ -320,7 +305,7 @@ export class IDBStorage {
|
||||
};
|
||||
});
|
||||
|
||||
const ourKnown: CoValueKnownState = {
|
||||
const ourKnown: CojsonInternalTypes.CoValueKnownState = {
|
||||
id: msg.id,
|
||||
header: true,
|
||||
sessions: {},
|
||||
@@ -389,11 +374,11 @@ export class IDBStorage {
|
||||
}
|
||||
}
|
||||
|
||||
handleKnown(msg: KnownStateMessage) {
|
||||
handleKnown(msg: CojsonInternalTypes.KnownStateMessage) {
|
||||
return this.sendNewContentAfter(msg, this.inTransaction("readonly"));
|
||||
}
|
||||
|
||||
handleDone(msg: DoneMessage) {}
|
||||
handleDone(_msg: CojsonInternalTypes.DoneMessage) {}
|
||||
|
||||
inTransaction(mode: "readwrite" | "readonly"): {
|
||||
coValues: IDBObjectStore;
|
||||
@@ -406,11 +391,13 @@ export class IDBStorage {
|
||||
);
|
||||
|
||||
tx.onerror = (event) => {
|
||||
const target = event.target as {
|
||||
error: DOMException;
|
||||
source?: { name: string };
|
||||
} | null;
|
||||
throw new Error(
|
||||
`Error in transaction (${
|
||||
(event.target as any).source?.name
|
||||
}): ${(event.target as any).error}`,
|
||||
{ cause: (event.target as any).error }
|
||||
`Error in transaction (${target?.source?.name}): ${target?.error}`,
|
||||
{ cause: target?.error }
|
||||
);
|
||||
};
|
||||
const coValues = tx.objectStore("coValues");
|
||||
|
||||
@@ -2,18 +2,12 @@
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"target": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"esModuleInterop": true,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user