Compare commits
10 Commits
react-perf
...
jazz-nodej
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34dda7bdbd | ||
|
|
49fa153581 | ||
|
|
c80b827775 | ||
|
|
a2bf9f988a | ||
|
|
ac27b2d5c2 | ||
|
|
c813518fdc | ||
|
|
d5034ed5c3 | ||
|
|
cf2c29a365 | ||
|
|
d948823db6 | ||
|
|
060ad4630d |
4
.github/workflows/build-and-deploy.yaml
vendored
4
.github/workflows/build-and-deploy.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
example: ["chat", "pets", "todo"]
|
example: ["chat", "pets", "todo", "inspector"]
|
||||||
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
|
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -105,7 +105,7 @@ jobs:
|
|||||||
needs: build-examples
|
needs: build-examples
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
example: ["chat", "pets", "todo"]
|
example: ["chat", "pets", "todo", "inspector"]
|
||||||
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
|
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
# jazz-example-chat
|
# jazz-example-chat
|
||||||
|
|
||||||
|
## 0.0.64
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
- jazz-react@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.0.63
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
- jazz-react@0.7.16
|
||||||
|
|
||||||
|
## 0.0.62
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-react@0.7.15
|
||||||
|
|
||||||
## 0.0.61
|
## 0.0.61
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jazz-example-chat",
|
"name": "jazz-example-chat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.61",
|
"version": "0.0.64",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# jazz-example-chat
|
# jazz-example-chat
|
||||||
|
|
||||||
|
## 0.0.48
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
- cojson-transport-ws@0.7.17
|
||||||
|
|
||||||
## 0.0.47
|
## 0.0.47
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "inspector",
|
"name": "jazz-inspector",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.47",
|
"version": "0.0.48",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"hash-slash": "workspace:*",
|
"hash-slash": "workspace:*",
|
||||||
"jazz-react": "workspace:*",
|
|
||||||
"jazz-react-auth-local": "workspace:*",
|
|
||||||
"cojson": "workspace:*",
|
"cojson": "workspace:*",
|
||||||
|
"cojson-transport-ws": "workspace:*",
|
||||||
|
"effect": "^3.1.5",
|
||||||
"lucide-react": "^0.274.0",
|
"lucide-react": "^0.274.0",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
309
examples/inspector/src/app.tsx
Normal file
309
examples/inspector/src/app.tsx
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import {
|
||||||
|
RawAccount,
|
||||||
|
CoID,
|
||||||
|
RawCoValue,
|
||||||
|
SessionID,
|
||||||
|
LocalNode,
|
||||||
|
AgentSecret,
|
||||||
|
AccountID,
|
||||||
|
cojsonInternals,
|
||||||
|
WasmCrypto,
|
||||||
|
} from "cojson";
|
||||||
|
import { clsx } from "clsx";
|
||||||
|
import { AccountInfo, CoJsonTree, Tag } from "./cojson-tree";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { createWebSocketPeer } from "cojson-transport-ws";
|
||||||
|
import { Effect } from "effect";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [accountID, setAccountID] = useState<CoID<RawAccount>>(
|
||||||
|
localStorage["inspectorAccountID"]
|
||||||
|
);
|
||||||
|
const [accountSecret, setAccountSecret] = useState<AgentSecret>(
|
||||||
|
localStorage["inspectorAccountSecret"]
|
||||||
|
);
|
||||||
|
|
||||||
|
const [coValueId, setCoValueId] = useState<CoID<RawCoValue>>(
|
||||||
|
window.location.hash.slice(2) as CoID<RawCoValue>
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("hashchange", () => {
|
||||||
|
setCoValueId(window.location.hash.slice(2) as CoID<RawCoValue>);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const [localNode, setLocalNode] = useState<LocalNode>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountID || !accountSecret) return;
|
||||||
|
WasmCrypto.create().then(async (crypto) => {
|
||||||
|
const wsPeer = await Effect.runPromise(
|
||||||
|
createWebSocketPeer({
|
||||||
|
id: "mesh",
|
||||||
|
websocket: new WebSocket("wss://mesh.jazz.tools"),
|
||||||
|
role: "server",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const node = await LocalNode.withLoadedAccount({
|
||||||
|
accountID: accountID,
|
||||||
|
accountSecret: accountSecret,
|
||||||
|
sessionID: cojsonInternals.newRandomSessionID(accountID),
|
||||||
|
peersToLoadFrom: [wsPeer],
|
||||||
|
crypto,
|
||||||
|
migration: async () => {
|
||||||
|
console.log("Not running any migration in inspector");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setLocalNode(node);
|
||||||
|
});
|
||||||
|
}, [accountID, accountSecret]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center w-screen h-screen p-2 gap-2">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
Account
|
||||||
|
<input
|
||||||
|
className="border p-2 rounded"
|
||||||
|
placeholder="Account ID"
|
||||||
|
value={accountID}
|
||||||
|
onChange={(e) => {
|
||||||
|
setAccountID(e.target.value as AccountID);
|
||||||
|
localStorage["inspectorAccountID"] = e.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
className="border p-2 rounded"
|
||||||
|
placeholder="Account Secret"
|
||||||
|
value={accountSecret}
|
||||||
|
onChange={(e) => {
|
||||||
|
setAccountSecret(e.target.value as AgentSecret);
|
||||||
|
localStorage["inspectorAccountSecret"] = e.target.value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{localNode ? (
|
||||||
|
<AccountInfo accountID={accountID} node={localNode} />
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
CoValue ID
|
||||||
|
<input
|
||||||
|
className="border p-2 rounded min-w-[20rem]"
|
||||||
|
placeholder="CoValue ID"
|
||||||
|
value={coValueId}
|
||||||
|
onChange={(e) =>
|
||||||
|
setCoValueId(e.target.value as CoID<RawCoValue>)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{coValueId && localNode ? (
|
||||||
|
<Inspect coValueId={coValueId} node={localNode} />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function ImageCoValue({ value }: { value: ImageDefinition["_shape"] }) {
|
||||||
|
// const keys = Object.keys(value);
|
||||||
|
// const keyIncludingRes = keys.find((key) => key.includes("x"));
|
||||||
|
// const idToResolve = keyIncludingRes
|
||||||
|
// ? value[keyIncludingRes as `${number}x${number}`]
|
||||||
|
// : null;
|
||||||
|
|
||||||
|
// if (!idToResolve) return <div>Can't find image</div>;
|
||||||
|
|
||||||
|
// const [blobURL, setBlobURL] = useState<string>();
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
|
||||||
|
// })
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <img
|
||||||
|
// src={image?.blobURL || value.placeholderDataURL}
|
||||||
|
// alt="placeholder"
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
function Inspect({
|
||||||
|
coValueId,
|
||||||
|
node,
|
||||||
|
}: {
|
||||||
|
coValueId: CoID<RawCoValue>;
|
||||||
|
node: LocalNode;
|
||||||
|
}) {
|
||||||
|
const [coValue, setCoValue] = useState<RawCoValue | "unavailable">();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return node.subscribe(coValueId, (coValue) => {
|
||||||
|
setCoValue(coValue);
|
||||||
|
});
|
||||||
|
}, [node, coValueId]);
|
||||||
|
|
||||||
|
if (coValue === "unavailable") {
|
||||||
|
return <div>Unavailable</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = coValue?.toJSON() || {};
|
||||||
|
const isImage =
|
||||||
|
typeof values === "object" && "placeholderDataURL" in values;
|
||||||
|
const isGroup = coValue?.core.header.ruleset?.type === "group";
|
||||||
|
|
||||||
|
const entires = Object.entries(values as any) as [string, string][];
|
||||||
|
const onlyCoValues = entires.filter(([key]) => key.startsWith("co_"));
|
||||||
|
|
||||||
|
let title = "";
|
||||||
|
if (isImage) {
|
||||||
|
title = "Image";
|
||||||
|
} else if (isGroup) {
|
||||||
|
title = "Group";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-auto">
|
||||||
|
<h1 className="text-xl font-bold mb-2">
|
||||||
|
Inspecting {title}{" "}
|
||||||
|
<span className="text-gray-500 text-sm">{coValueId}</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{isGroup ? (
|
||||||
|
<p>
|
||||||
|
{onlyCoValues.length > 0 ? <h3>Permissions</h3> : ""}
|
||||||
|
<div className="flex gap-2 flex-col">
|
||||||
|
{onlyCoValues?.map(([key, value]) => (
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
<span className="bg-gray-200 text-xs px-2 py-0.5 rounded">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
<AccountInfo
|
||||||
|
accountID={key as CoID<RawAccount>}
|
||||||
|
node={node}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<span className="">
|
||||||
|
Group{" "}
|
||||||
|
<Tag href={`#/${coValue?.group.id}`}>
|
||||||
|
{coValue?.group.id}
|
||||||
|
</Tag>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{/* {isImage ? (
|
||||||
|
<div className="my-2">
|
||||||
|
<ImageCoValue value={values as any} />
|
||||||
|
</div>
|
||||||
|
) : null} */}
|
||||||
|
<pre className="max-w-[80vw] overflow-scroll text-sm mt-4">
|
||||||
|
<CoJsonTree coValueId={coValueId} node={node} />
|
||||||
|
</pre>
|
||||||
|
<h2 className="text-lg font-semibold mt-10 mb-4">Sessions</h2>
|
||||||
|
{coValue && <Sessions coValue={coValue} node={node} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sessions({ coValue, node }: { coValue: RawCoValue; node: LocalNode }) {
|
||||||
|
const validTx = coValue.core.getValidSortedTransactions();
|
||||||
|
return (
|
||||||
|
<div className="max-w-[80vw] border rounded">
|
||||||
|
{[...coValue.core.sessionLogs.entries()].map(
|
||||||
|
([sessionID, session]) => (
|
||||||
|
<div
|
||||||
|
key={sessionID}
|
||||||
|
className="mv-10 flex gap-2 border-b p-5 flex-wrap flex-col"
|
||||||
|
>
|
||||||
|
<div className="flex gap-2 flex-row">
|
||||||
|
<SessionInfo
|
||||||
|
sessionID={sessionID}
|
||||||
|
transactionCount={session.transactions.length}
|
||||||
|
node={node}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-1 flex-wrap max-h-64 overflow-y-auto p-1 bg-gray-50 rounded">
|
||||||
|
{session.transactions.map((tx, txIdx) => {
|
||||||
|
const correspondingValidTx = validTx.find(
|
||||||
|
(validTx) =>
|
||||||
|
validTx.txID.sessionID === sessionID &&
|
||||||
|
validTx.txID.txIndex == txIdx
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={txIdx}
|
||||||
|
className={clsx(
|
||||||
|
"text-xs flex-1 p-2 border rounded min-w-36 max-w-40 overflow-scroll bg-white",
|
||||||
|
!correspondingValidTx &&
|
||||||
|
"bg-red-50 border-red-100"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{new Date(
|
||||||
|
tx.madeAt
|
||||||
|
).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
<div>{tx.privacy}</div>
|
||||||
|
<pre>
|
||||||
|
{correspondingValidTx
|
||||||
|
? JSON.stringify(
|
||||||
|
correspondingValidTx.changes,
|
||||||
|
undefined,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
: "invalid/undecryptable"}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs">
|
||||||
|
{session.lastHash} / {session.lastSignature}{" "}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SessionInfo({
|
||||||
|
sessionID,
|
||||||
|
transactionCount,
|
||||||
|
node,
|
||||||
|
}: {
|
||||||
|
sessionID: SessionID;
|
||||||
|
transactionCount: number;
|
||||||
|
node: LocalNode;
|
||||||
|
}) {
|
||||||
|
let Prefix = sessionID.startsWith("co_") ? (
|
||||||
|
<AccountInfo
|
||||||
|
accountID={sessionID.split("_session_")[0] as CoID<RawAccount>}
|
||||||
|
node={node}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<pre className="text-xs">{sessionID.split("_session_")[0]}</pre>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{Prefix}
|
||||||
|
<div>
|
||||||
|
<span className="text-xs">
|
||||||
|
Session {sessionID.split("_session_")[1]}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-gray-600 font-medium">
|
||||||
|
{" "}
|
||||||
|
- {transactionCount} txs
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
249
examples/inspector/src/cojson-tree.tsx
Normal file
249
examples/inspector/src/cojson-tree.tsx
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
|
import { AccountID, CoID, LocalNode, RawAccount, RawCoMap, RawCoValue } from "cojson";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { LinkIcon } from "./link-icon";
|
||||||
|
|
||||||
|
export function CoJsonTree({
|
||||||
|
coValueId,
|
||||||
|
node,
|
||||||
|
}: {
|
||||||
|
coValueId: CoID<RawCoValue>;
|
||||||
|
node: LocalNode;
|
||||||
|
}) {
|
||||||
|
const [coValue, setCoValue] = useState<RawCoValue | "unavailable">();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return node.subscribe(coValueId, (value) => {
|
||||||
|
setCoValue(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (coValue === "unavailable") {
|
||||||
|
return <div className="text-red-500">Unavailable</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = coValue?.toJSON() || {};
|
||||||
|
|
||||||
|
return <RenderCoValueJSON json={values} node={node} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenderObject({
|
||||||
|
json,
|
||||||
|
node,
|
||||||
|
}: {
|
||||||
|
json: Record<string, any>;
|
||||||
|
node: LocalNode;
|
||||||
|
}) {
|
||||||
|
const [limit, setLimit] = useState(10);
|
||||||
|
const hasMore = Object.keys(json).length > limit;
|
||||||
|
|
||||||
|
const entries = Object.entries(json).slice(0, limit);
|
||||||
|
return (
|
||||||
|
<div className="flex gap-x-1 flex-col font-mono text-xs overflow-auto">
|
||||||
|
{"{"}
|
||||||
|
{entries.map(([key, value]) => {
|
||||||
|
return (
|
||||||
|
<RenderObjectValue
|
||||||
|
property={key}
|
||||||
|
value={value}
|
||||||
|
node={node}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{hasMore ? (
|
||||||
|
<div
|
||||||
|
className="text-gray-500 cursor-pointer"
|
||||||
|
onClick={() => setLimit((l) => l + 10)}
|
||||||
|
>
|
||||||
|
... {Object.keys(json).length - limit} more
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{"}"}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenderObjectValue({
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
node,
|
||||||
|
}: {
|
||||||
|
property: string;
|
||||||
|
value: any;
|
||||||
|
node: LocalNode;
|
||||||
|
}) {
|
||||||
|
const [shouldLoad, setShouldLoad] = useState(false);
|
||||||
|
|
||||||
|
const isCoValue =
|
||||||
|
typeof value === "string" ? value?.startsWith("co_") : false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx(`flex group`)}>
|
||||||
|
<div className="text-gray-500 flex items-start">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<RenderCoValueJSON json={property} node={node} />:{" "}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isCoValue ? (
|
||||||
|
<div className={clsx(shouldLoad && "pb-2")}>
|
||||||
|
<div className="flex items-center ">
|
||||||
|
<div onClick={() => setShouldLoad((s) => !s)}>
|
||||||
|
<div className="w-8 text-center text-gray-700 font-mono px-1 text-xs rounded hover:bg-gray-300 cursor-pointer">
|
||||||
|
{shouldLoad ? `-` : `...`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={`#/${value}`}
|
||||||
|
className="ml-2 group-hover:block hidden"
|
||||||
|
>
|
||||||
|
<LinkIcon />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span>
|
||||||
|
{shouldLoad ? (
|
||||||
|
<CoJsonTree coValueId={value} node={node} />
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="">
|
||||||
|
<RenderCoValueJSON json={value} node={node} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenderCoValueArray({ json, node }: { json: any[]; node: LocalNode }) {
|
||||||
|
const [limit, setLimit] = useState(10);
|
||||||
|
const hasMore = json.length > limit;
|
||||||
|
|
||||||
|
const entries = json.slice(0, limit);
|
||||||
|
return (
|
||||||
|
<div className="flex gap-x-1 flex-col font-mono text-xs overflow-auto">
|
||||||
|
{entries.map((value, idx) => {
|
||||||
|
return (
|
||||||
|
<div key={idx} className="flex gap-x-1">
|
||||||
|
<RenderCoValueJSON json={value} node={node} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{hasMore ? (
|
||||||
|
<div
|
||||||
|
className="text-gray-500 cursor-pointer"
|
||||||
|
onClick={() => setLimit((l) => l + 10)}
|
||||||
|
>
|
||||||
|
... {json.length - limit} more
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenderCoValueJSON({
|
||||||
|
json,
|
||||||
|
node,
|
||||||
|
}: {
|
||||||
|
json:
|
||||||
|
| Record<string, any>
|
||||||
|
| any[]
|
||||||
|
| string
|
||||||
|
| null
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| undefined;
|
||||||
|
node: LocalNode;
|
||||||
|
}) {
|
||||||
|
if (typeof json === "undefined") {
|
||||||
|
return <>"undefined"</>;
|
||||||
|
} else if (Array.isArray(json)) {
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<span className="text-gray-500">[</span>
|
||||||
|
<div className="ml-2">
|
||||||
|
<RenderCoValueArray json={json} node={node} />
|
||||||
|
</div>
|
||||||
|
<span className="text-gray-500">]</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
typeof json === "object" &&
|
||||||
|
json &&
|
||||||
|
Object.getPrototypeOf(json) === Object.prototype
|
||||||
|
) {
|
||||||
|
return <RenderObject json={json} node={node} />;
|
||||||
|
} else if (typeof json === "string") {
|
||||||
|
if (json?.startsWith("co_")) {
|
||||||
|
if (json.includes("_session_")) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AccountInfo accountID={json.split("_session_")[0] as AccountID} node={node}/>{" "}
|
||||||
|
(sess {json.split("_session_")[1]})
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<a className="underline" href={`#/${json}`}>
|
||||||
|
{'"'}
|
||||||
|
{json}
|
||||||
|
{'"'}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return <div className="truncate max-w-64 ml-1">{json}</div>;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return <div className="truncate max-w-64">{JSON.stringify(json)}</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccountInfo({ accountID, node }: { accountID: CoID<RawAccount>, node: LocalNode }) {
|
||||||
|
const [name, setName] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const account = await node.load(accountID);
|
||||||
|
if (account === "unavailable") return;
|
||||||
|
const profileID = account?.get("profile");
|
||||||
|
if (profileID === undefined) return;
|
||||||
|
const profile = await node.load(profileID as CoID<RawCoMap>);
|
||||||
|
if (profile === "unavailable") return;
|
||||||
|
setName(profile?.get("name") as string);
|
||||||
|
})()
|
||||||
|
}, [accountID, node]);
|
||||||
|
|
||||||
|
return name ? (
|
||||||
|
<Tag href={`#/${accountID}`} title={accountID}><h1>{name}</h1></Tag>
|
||||||
|
) : (
|
||||||
|
<Tag href={`#/${accountID}`}>{accountID}</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Tag({
|
||||||
|
children,
|
||||||
|
href,
|
||||||
|
title
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
href?: string;
|
||||||
|
title?: string;
|
||||||
|
}) {
|
||||||
|
if (href) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
className="border text-xs px-2 py-0.5 rounded hover:underline"
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span className="border text-xs px-2 py-0.5 rounded">{children}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,29 @@
|
|||||||
# jazz-example-pets
|
# jazz-example-pets
|
||||||
|
|
||||||
|
## 0.0.82
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- jazz-react@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
- jazz-browser-media-images@0.7.17
|
||||||
|
|
||||||
|
## 0.0.81
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
- jazz-browser-media-images@0.7.16
|
||||||
|
- jazz-react@0.7.16
|
||||||
|
|
||||||
|
## 0.0.80
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-react@0.7.15
|
||||||
|
|
||||||
## 0.0.79
|
## 0.0.79
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jazz-example-pets",
|
"name": "jazz-example-pets",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.79",
|
"version": "0.0.82",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
# jazz-example-todo
|
# jazz-example-todo
|
||||||
|
|
||||||
|
## 0.0.81
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- jazz-react@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.0.80
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
- jazz-react@0.7.16
|
||||||
|
|
||||||
|
## 0.0.79
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-react@0.7.15
|
||||||
|
|
||||||
## 0.0.78
|
## 0.0.78
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jazz-example-todo",
|
"name": "jazz-example-todo",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.78",
|
"version": "0.0.81",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import localFont from "next/font/local";
|
|||||||
import { GcmpLogo, JazzLogo } from "@/components/logos";
|
import { GcmpLogo, JazzLogo } from "@/components/logos";
|
||||||
import { SiGithub, SiDiscord, SiTwitter } from "@icons-pack/react-simple-icons";
|
import { SiGithub, SiDiscord, SiTwitter } from "@icons-pack/react-simple-icons";
|
||||||
import { Nav, NavLink, Newsletter, NewsletterButton } from "@/components/nav";
|
import { Nav, NavLink, Newsletter, NewsletterButton } from "@/components/nav";
|
||||||
import { MailIcon } from "lucide-react";
|
|
||||||
import { DocNav } from "@/components/docs/nav";
|
import { DocNav } from "@/components/docs/nav";
|
||||||
|
|
||||||
|
import { SpeedInsights } from "@vercel/speed-insights/next"
|
||||||
|
import { Analytics } from "@vercel/analytics/react"
|
||||||
|
|
||||||
// If loading a variable font, you don't need to specify the font weight
|
// If loading a variable font, you don't need to specify the font weight
|
||||||
const manrope = Manrope({
|
const manrope = Manrope({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
@@ -48,6 +50,8 @@ export default function RootLayout({
|
|||||||
"flex flex-col items-center bg-stone-50 dark:bg-stone-950 overflow-x-hidden",
|
"flex flex-col items-center bg-stone-50 dark:bg-stone-950 overflow-x-hidden",
|
||||||
].join(" ")}
|
].join(" ")}
|
||||||
>
|
>
|
||||||
|
<SpeedInsights/>
|
||||||
|
<Analytics/>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
defaultTheme="system"
|
defaultTheme="system"
|
||||||
@@ -192,12 +196,6 @@ export default function RootLayout({
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
<script
|
|
||||||
defer
|
|
||||||
data-api="/api/event"
|
|
||||||
data-domain="jazz.tools"
|
|
||||||
src="/js/script.js"
|
|
||||||
></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"*.{ts,tsx}": "eslint --fix",
|
"*.{ts,tsx}": "eslint --fix",
|
||||||
"*.{js,jsx,mdx,json}": "prettier --write"
|
"*.{js,jsx,mdx,json}": "prettier --write"
|
||||||
},
|
},
|
||||||
|
"packageManager": "pnpm@9.1.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@evilmartians/harmony": "^1.0.0",
|
"@evilmartians/harmony": "^1.0.0",
|
||||||
"@icons-pack/react-simple-icons": "^9.1.0",
|
"@icons-pack/react-simple-icons": "^9.1.0",
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
"@mdx-js/react": "^2.3.0",
|
"@mdx-js/react": "^2.3.0",
|
||||||
"@next/mdx": "^13.5.4",
|
"@next/mdx": "^13.5.4",
|
||||||
"@types/mdx": "^2.0.8",
|
"@types/mdx": "^2.0.8",
|
||||||
|
"@vercel/analytics": "^1.3.1",
|
||||||
|
"@vercel/speed-insights": "^1.0.12",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"lucide-react": "^0.284.0",
|
"lucide-react": "^0.284.0",
|
||||||
|
|||||||
5244
homepage/pnpm-lock.yaml
generated
5244
homepage/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,243 +0,0 @@
|
|||||||
import {
|
|
||||||
WithJazz,
|
|
||||||
useJazz,
|
|
||||||
DemoAuth,
|
|
||||||
useAutoSub,
|
|
||||||
useBinaryStream,
|
|
||||||
} from "jazz-react";
|
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
import { HashRoute } from "hash-slash";
|
|
||||||
import { Account, CoID, CoValue, SessionID } from "cojson";
|
|
||||||
import { clsx } from "clsx";
|
|
||||||
import { ImageDefinition } from "cojson/src/media";
|
|
||||||
import { CoJsonTree } from "./cojson-tree";
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
||||||
<WithJazz
|
|
||||||
auth={DemoAuth({ appName: "Jazz Chat Example" })}
|
|
||||||
apiKey="api_z9d034j3t34ht034ir"
|
|
||||||
>
|
|
||||||
<App />
|
|
||||||
</WithJazz>
|
|
||||||
);
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col items-center justify-between w-screen h-screen p-2 ">
|
|
||||||
<button
|
|
||||||
onClick={useJazz().logOut}
|
|
||||||
className="rounded mb-5 px-2 py-1 bg-stone-200 dark:bg-stone-800 dark:text-white self-end"
|
|
||||||
>
|
|
||||||
Log Out
|
|
||||||
</button>
|
|
||||||
{HashRoute(
|
|
||||||
{
|
|
||||||
"/": <Home />,
|
|
||||||
"/:id": (id) => <Inspect coValueId={id as CoID<CoValue>} />,
|
|
||||||
},
|
|
||||||
{ reportToParentFrame: true }
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Home() {
|
|
||||||
return (
|
|
||||||
<form
|
|
||||||
className="mb-auto"
|
|
||||||
onSubmit={(event) => {
|
|
||||||
const coValueId = (event.target as any).coValueId
|
|
||||||
.value as CoID<CoValue>;
|
|
||||||
location.hash = "/" + coValueId;
|
|
||||||
event.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input name="coValueId" className="border" />
|
|
||||||
<button>Inspect</button>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Tag({ children, href }: { children: React.ReactNode; href?: string }) {
|
|
||||||
if (href) {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
href={href}
|
|
||||||
className="border text-xs px-2 py-0.5 rounded hover:underline"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <span className="border text-xs px-2 py-0.5 rounded">{children}</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ImageCoValue({ value }: { value: ImageDefinition["_shape"] }) {
|
|
||||||
const keys = Object.keys(value);
|
|
||||||
const keyIncludingRes = keys.find((key) => key.includes("x"));
|
|
||||||
const idToResolve = keyIncludingRes
|
|
||||||
? value[keyIncludingRes as `${number}x${number}`]
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (!idToResolve) return <div>Can't find image</div>;
|
|
||||||
|
|
||||||
const image = useBinaryStream(idToResolve);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<img src={image?.blobURL || value.placeholderDataURL} alt="placeholder" />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Inspect({ coValueId }: { coValueId: CoID<CoValue> }) {
|
|
||||||
const coValue = useAutoSub(coValueId);
|
|
||||||
|
|
||||||
const values = coValue?.meta.coValue.toJSON() || {};
|
|
||||||
const isImage = "placeholderDataURL" in values;
|
|
||||||
const isGroup = coValue?.meta.group.id === coValueId;
|
|
||||||
|
|
||||||
const entires = Object.entries(values as any) as [string, string][];
|
|
||||||
const onlyCoValues = entires.filter(([key]) => key.startsWith("co_"));
|
|
||||||
|
|
||||||
let title = "";
|
|
||||||
if (isImage) {
|
|
||||||
title = "Image";
|
|
||||||
} else if (isGroup) {
|
|
||||||
title = "Group";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mb-auto">
|
|
||||||
<h1 className="text-xl font-bold mb-2">
|
|
||||||
Inspecting {title}{" "}
|
|
||||||
<span className="text-gray-500 text-sm">{coValueId}</span>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{isGroup ? (
|
|
||||||
<p>
|
|
||||||
{onlyCoValues.length > 0 ? <h3>Permissions</h3> : ""}
|
|
||||||
<div className="flex gap-2 flex-col">
|
|
||||||
{onlyCoValues?.map(([key, value]) => (
|
|
||||||
<div className="flex gap-1 items-center">
|
|
||||||
<span className="bg-gray-200 text-xs px-2 py-0.5 rounded">
|
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
<AccountInfo accountID={key as CoID<Account>} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<span className="">
|
|
||||||
Group{" "}
|
|
||||||
<Tag href={`#/${coValue?.meta.group.id}`}>
|
|
||||||
{coValue?.meta.group.id}
|
|
||||||
</Tag>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{isImage ? (
|
|
||||||
<div className="my-2">
|
|
||||||
<ImageCoValue value={values as any} />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<pre className="max-w-[80vw] overflow-scroll text-sm mt-4">
|
|
||||||
<CoJsonTree coValueId={coValueId} />
|
|
||||||
</pre>
|
|
||||||
<h2 className="text-lg font-semibold mt-10 mb-4">Sessions</h2>
|
|
||||||
{coValue && <Sessions coValue={coValue.meta.coValue} />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Sessions({ coValue }: { coValue: CoValue }) {
|
|
||||||
const validTx = coValue.core.getValidSortedTransactions();
|
|
||||||
return (
|
|
||||||
<div className="max-w-[80vw] border rounded">
|
|
||||||
{[...coValue.core.sessionLogs.entries()].map(([sessionID, session]) => (
|
|
||||||
<div
|
|
||||||
key={sessionID}
|
|
||||||
className="mv-10 flex gap-2 border-b p-5 flex-wrap flex-col"
|
|
||||||
>
|
|
||||||
<div className="flex gap-2 flex-row">
|
|
||||||
<SessionInfo
|
|
||||||
sessionID={sessionID}
|
|
||||||
transactionCount={session.transactions.length}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-1 flex-wrap max-h-64 overflow-y-auto p-1 bg-gray-50 rounded">
|
|
||||||
{session.transactions.map((tx, txIdx) => {
|
|
||||||
const correspondingValidTx = validTx.find(
|
|
||||||
(validTx) =>
|
|
||||||
validTx.txID.sessionID === sessionID &&
|
|
||||||
validTx.txID.txIndex == txIdx
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={txIdx}
|
|
||||||
className={clsx(
|
|
||||||
"text-xs flex-1 p-2 border rounded min-w-36 max-w-40 overflow-scroll bg-white",
|
|
||||||
!correspondingValidTx && "bg-red-50 border-red-100"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div>{new Date(tx.madeAt).toLocaleString()}</div>
|
|
||||||
<div>{tx.privacy}</div>
|
|
||||||
<pre>
|
|
||||||
{correspondingValidTx
|
|
||||||
? JSON.stringify(
|
|
||||||
correspondingValidTx.changes,
|
|
||||||
undefined,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
: "invalid/undecryptable"}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="text-xs">
|
|
||||||
{session.lastHash} / {session.lastSignature}{" "}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SessionInfo({
|
|
||||||
sessionID,
|
|
||||||
transactionCount,
|
|
||||||
}: {
|
|
||||||
sessionID: SessionID;
|
|
||||||
transactionCount: number;
|
|
||||||
}) {
|
|
||||||
let Prefix = sessionID.startsWith("co_") ? (
|
|
||||||
<AccountInfo accountID={sessionID.split("_session_")[0] as CoID<Account>} />
|
|
||||||
) : (
|
|
||||||
<pre className="text-xs">{sessionID.split("_session_")[0]}</pre>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{Prefix}
|
|
||||||
<div>
|
|
||||||
<span className="text-xs">
|
|
||||||
Session {sessionID.split("_session_")[1]}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-gray-600 font-medium">
|
|
||||||
{" "}
|
|
||||||
- {transactionCount} txs
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function AccountInfo({ accountID }: { accountID: CoID<Account> }) {
|
|
||||||
const account = useAutoSub(accountID);
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<h1>{account?.profile?.name}</h1>
|
|
||||||
|
|
||||||
<Tag href={`#/${accountID}`}>{account?.id}</Tag>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
import clsx from "clsx";
|
|
||||||
import { CoID, CoValue } from "cojson";
|
|
||||||
import { useAutoSub } from "jazz-react";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { LinkIcon } from "./link-icon";
|
|
||||||
|
|
||||||
export function CoJsonTree({ coValueId }: { coValueId: CoID<CoValue> }) {
|
|
||||||
const coValue = useAutoSub(coValueId);
|
|
||||||
|
|
||||||
const values = coValue?.meta.coValue.toJSON() || {};
|
|
||||||
|
|
||||||
return <RenderCoValueJSON json={values} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenderObject({ json }: { json: Record<string, any> }) {
|
|
||||||
const [limit, setLimit] = useState(10);
|
|
||||||
const hasMore = Object.keys(json).length > limit;
|
|
||||||
|
|
||||||
const entries = Object.entries(json).slice(0, limit);
|
|
||||||
return (
|
|
||||||
<div className="flex gap-x-1 flex-col font-mono text-xs overflow-auto">
|
|
||||||
{entries.map(([key, value]) => {
|
|
||||||
return <RenderObjectValue property={key} value={value} />;
|
|
||||||
})}
|
|
||||||
{hasMore ? (
|
|
||||||
<div
|
|
||||||
className="text-gray-500 cursor-pointer"
|
|
||||||
onClick={() => setLimit((l) => l + 10)}
|
|
||||||
>
|
|
||||||
... {Object.keys(json).length - limit} more
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenderObjectValue({
|
|
||||||
property,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
property: string;
|
|
||||||
value: any;
|
|
||||||
}) {
|
|
||||||
const [shouldLoad, setShouldLoad] = useState(false);
|
|
||||||
|
|
||||||
const isCoValue =
|
|
||||||
typeof value === "string" ? value?.startsWith("co_") : false;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={clsx(`flex group`)}>
|
|
||||||
<span className="text-gray-500 flex">
|
|
||||||
<RenderCoValueJSON json={property} />:{" "}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{isCoValue ? (
|
|
||||||
<div className={clsx(shouldLoad && "pb-2")}>
|
|
||||||
<div className="flex items-center ">
|
|
||||||
<div onClick={() => setShouldLoad((s) => !s)}>
|
|
||||||
<div className="w-8 text-center text-gray-700 font-mono px-1 text-xs rounded hover:bg-gray-300 cursor-pointer">
|
|
||||||
{shouldLoad ? `-` : `...`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a href={`#/${value}`} className="ml-2 group-hover:block hidden">
|
|
||||||
<LinkIcon />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<span>{shouldLoad ? <CoJsonTree coValueId={value} /> : null}</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="">
|
|
||||||
<RenderCoValueJSON json={value} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenderCoValueArray({ json }: { json: any[] }) {
|
|
||||||
const [limit, setLimit] = useState(10);
|
|
||||||
const hasMore = json.length > limit;
|
|
||||||
|
|
||||||
const entries = json.slice(0, limit);
|
|
||||||
return (
|
|
||||||
<div className="flex gap-x-1 flex-col font-mono text-xs overflow-auto">
|
|
||||||
{entries.map((value, idx) => {
|
|
||||||
return (
|
|
||||||
<div key={idx} className="flex gap-x-1">
|
|
||||||
<RenderCoValueJSON json={value} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{hasMore ? (
|
|
||||||
<div
|
|
||||||
className="text-gray-500 cursor-pointer"
|
|
||||||
onClick={() => setLimit((l) => l + 10)}
|
|
||||||
>
|
|
||||||
... {json.length - limit} more
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RenderCoValueJSON({
|
|
||||||
json,
|
|
||||||
}: {
|
|
||||||
json:
|
|
||||||
| Record<string, any>
|
|
||||||
| any[]
|
|
||||||
| string
|
|
||||||
| null
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| undefined;
|
|
||||||
}) {
|
|
||||||
if (typeof json === "undefined") {
|
|
||||||
return <>"undefined"</>;
|
|
||||||
} else if (Array.isArray(json)) {
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<span className="text-gray-500">[</span>
|
|
||||||
<div className="ml-2">
|
|
||||||
<RenderCoValueArray json={json} />
|
|
||||||
</div>
|
|
||||||
<span className="text-gray-500">]</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
typeof json === "object" &&
|
|
||||||
json &&
|
|
||||||
Object.getPrototypeOf(json) === Object.prototype
|
|
||||||
) {
|
|
||||||
return <RenderObject json={json} />;
|
|
||||||
} else if (typeof json === "string") {
|
|
||||||
if (json?.startsWith("co_")) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<a className="underline" href={`#/${json}`}>
|
|
||||||
{'"'}
|
|
||||||
{json}
|
|
||||||
{'"'}
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <div className="truncate max-w-64 ml-1">{json}</div>;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return <div className="truncate max-w-64">{JSON.stringify(json)}</div>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"packages/*",
|
"packages/*",
|
||||||
"examples/*"
|
"examples/*"
|
||||||
],
|
],
|
||||||
|
"packageManager": "pnpm@9.1.4",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/cli": "^2.27.3",
|
"@changesets/cli": "^2.27.3",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# cojson-storage-indexeddb
|
# cojson-storage-indexeddb
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cojson-storage-indexeddb",
|
"name": "cojson-storage-indexeddb",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# cojson-storage-sqlite
|
# cojson-storage-sqlite
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "cojson-storage-sqlite",
|
"name": "cojson-storage-sqlite",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# cojson-transport-nodejs-ws
|
# cojson-transport-nodejs-ws
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "cojson-transport-ws",
|
"name": "cojson-transport-ws",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# cojson
|
# cojson
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Fix bugs in new storage interface
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# jazz-browser-media-images
|
# jazz-browser-media-images
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- jazz-browser@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.7.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
- jazz-browser@0.7.16
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jazz-browser-media-images",
|
"name": "jazz-browser-media-images",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
# jazz-browser
|
# jazz-browser
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
- cojson-storage-indexeddb@0.7.17
|
||||||
|
- cojson-transport-ws@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.7.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jazz-browser",
|
"name": "jazz-browser",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# jazz-autosub
|
# jazz-autosub
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
- cojson-transport-ws@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.7.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cojson": "workspace:*",
|
"cojson": "workspace:*",
|
||||||
"cojson-transport-ws": "workspace:*",
|
"cojson-transport-ws": "workspace:*",
|
||||||
|
|||||||
@@ -1,5 +1,28 @@
|
|||||||
# jazz-react
|
# jazz-react
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
- jazz-browser@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.7.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
- jazz-browser@0.7.16
|
||||||
|
|
||||||
|
## 0.7.15
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Provide current res in ProgressiveImg
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jazz-react",
|
"name": "jazz-react",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
|
|||||||
@@ -127,20 +127,20 @@ export function createJazzReactContext<Acc extends Account>({
|
|||||||
id: ID<V> | undefined,
|
id: ID<V> | undefined,
|
||||||
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
||||||
): DeeplyLoaded<V, D> | undefined {
|
): DeeplyLoaded<V, D> | undefined {
|
||||||
const [state, setState] = useState<DeeplyLoaded<V, D> | undefined>(
|
const [state, setState] = useState<{
|
||||||
undefined,
|
value: DeeplyLoaded<V, D> | undefined;
|
||||||
);
|
}>({ value: undefined });
|
||||||
const me = React.useContext(JazzContext)?.me;
|
const me = React.useContext(JazzContext)?.me;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!id || !me) return;
|
if (!id || !me) return;
|
||||||
|
|
||||||
return subscribeToCoValue(Schema, id, me, depth, (value) => {
|
return subscribeToCoValue(Schema, id, me, depth, (value) => {
|
||||||
setState(value);
|
setState({ value });
|
||||||
});
|
});
|
||||||
}, [Schema, id, me]);
|
}, [Schema, id, me]);
|
||||||
|
|
||||||
return state;
|
return state.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useAcceptInvite<V extends CoValue>({
|
function useAcceptInvite<V extends CoValue>({
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ export function useProgressiveImg({
|
|||||||
image: ImageDefinition | null | undefined;
|
image: ImageDefinition | null | undefined;
|
||||||
maxWidth?: number;
|
maxWidth?: number;
|
||||||
}) {
|
}) {
|
||||||
const [src, setSrc] = useState<string | undefined>(undefined);
|
const [current, setCurrent] = useState<
|
||||||
|
| { src?: string; res?: `${number}x${number}` | "placeholder" }
|
||||||
|
| undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let lastHighestRes: string | undefined;
|
let lastHighestRes: string | undefined;
|
||||||
@@ -22,21 +25,28 @@ export function useProgressiveImg({
|
|||||||
const blob = highestRes.stream.toBlob();
|
const blob = highestRes.stream.toBlob();
|
||||||
if (blob) {
|
if (blob) {
|
||||||
const blobURI = URL.createObjectURL(blob);
|
const blobURI = URL.createObjectURL(blob);
|
||||||
setSrc(blobURI);
|
setCurrent({ src: blobURI, res: highestRes.res });
|
||||||
return () => {
|
return () => {
|
||||||
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
|
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setSrc(update?.placeholderDataURL);
|
setCurrent({
|
||||||
|
src: update?.placeholderDataURL,
|
||||||
|
res: "placeholder",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return unsub;
|
return unsub;
|
||||||
}, [image?.id, maxWidth]);
|
}, [image?.id, maxWidth]);
|
||||||
|
|
||||||
return { src, originalSize: image?.originalSize };
|
return {
|
||||||
|
src: current?.src,
|
||||||
|
res: current?.res,
|
||||||
|
originalSize: image?.originalSize,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @category Media */
|
/** @category Media */
|
||||||
@@ -47,6 +57,7 @@ export function ProgressiveImg({
|
|||||||
}: {
|
}: {
|
||||||
children: (result: {
|
children: (result: {
|
||||||
src: string | undefined;
|
src: string | undefined;
|
||||||
|
res: `${number}x${number}` | "placeholder" | undefined;
|
||||||
originalSize: readonly [number, number] | undefined;
|
originalSize: readonly [number, number] | undefined;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
image: ImageDefinition | null | undefined;
|
image: ImageDefinition | null | undefined;
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
# jazz-autosub
|
# jazz-autosub
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
- cojson-transport-ws@0.7.17
|
||||||
|
- jazz-tools@0.7.17
|
||||||
|
|
||||||
|
## 0.7.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- jazz-tools@0.7.16
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"bin": "./dist/index.js",
|
"bin": "./dist/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --ext ts,tsx",
|
"lint": "eslint . --ext ts,tsx",
|
||||||
"format": "prettier --write './src/**/*.{ts,tsx}'",
|
"format": "prettier --write './src/**/*.{ts,tsx}'",
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
# jazz-autosub
|
# jazz-autosub
|
||||||
|
|
||||||
|
## 0.7.17
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies
|
||||||
|
- cojson@0.7.17
|
||||||
|
|
||||||
|
## 0.7.16
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Fix: allow null in encoded fields
|
||||||
|
|
||||||
## 0.7.14
|
## 0.7.14
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"types": "./src/index.ts",
|
"types": "./src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "0.7.14",
|
"version": "0.7.17",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/schema": "^0.66.16",
|
"@effect/schema": "^0.66.16",
|
||||||
"cojson": "workspace:*",
|
"cojson": "workspace:*",
|
||||||
|
|||||||
@@ -539,6 +539,7 @@ const CoMapProxyHandler: ProxyHandler<CoMap> = {
|
|||||||
if (
|
if (
|
||||||
(typeof key === "string" || ItemsSym) &&
|
(typeof key === "string" || ItemsSym) &&
|
||||||
typeof value === "object" &&
|
typeof value === "object" &&
|
||||||
|
value !== null &&
|
||||||
SchemaInit in value
|
SchemaInit in value
|
||||||
) {
|
) {
|
||||||
(target.constructor as typeof CoMap)._schema ||= {};
|
(target.constructor as typeof CoMap)._schema ||= {};
|
||||||
|
|||||||
@@ -15,70 +15,6 @@ export const subscriptionsScopes = new WeakMap<
|
|||||||
|
|
||||||
const TRACE_INVALIDATIONS = false;
|
const TRACE_INVALIDATIONS = false;
|
||||||
|
|
||||||
let nSeconds = 1;
|
|
||||||
setInterval(() => console.log(nSeconds++ + "s passed"), 1000);
|
|
||||||
|
|
||||||
export interface ThrottleOptions {
|
|
||||||
/**
|
|
||||||
* Fire immediately on the first call.
|
|
||||||
*/
|
|
||||||
start?: boolean;
|
|
||||||
/**
|
|
||||||
* Fire as soon as `wait` has passed.
|
|
||||||
*/
|
|
||||||
middle?: boolean;
|
|
||||||
/**
|
|
||||||
* Cancel after the first successful call.
|
|
||||||
*/
|
|
||||||
once?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Throttler<T extends unknown[]> {
|
|
||||||
(...args: T): void;
|
|
||||||
cancel(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function throttle<T extends unknown[]>(
|
|
||||||
callback: (...args: T) => unknown,
|
|
||||||
wait = 0,
|
|
||||||
{ start = true, middle = true, once = false }: ThrottleOptions = {},
|
|
||||||
): Throttler<T> {
|
|
||||||
let innerStart = start;
|
|
||||||
let last = 0;
|
|
||||||
let timer: ReturnType<typeof setTimeout>;
|
|
||||||
let cancelled = false;
|
|
||||||
function fn(this: unknown, ...args: T) {
|
|
||||||
if (cancelled) return;
|
|
||||||
const delta = Date.now() - last;
|
|
||||||
last = Date.now();
|
|
||||||
|
|
||||||
if (start && middle && delta >= wait) {
|
|
||||||
innerStart = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (innerStart) {
|
|
||||||
innerStart = false;
|
|
||||||
callback.apply(this, args);
|
|
||||||
if (once) fn.cancel();
|
|
||||||
} else if ((middle && delta < wait) || !middle) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
timer = setTimeout(
|
|
||||||
() => {
|
|
||||||
last = Date.now();
|
|
||||||
callback.apply(this, args);
|
|
||||||
if (once) fn.cancel();
|
|
||||||
},
|
|
||||||
!middle ? wait : wait - delta,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn.cancel = () => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SubscriptionScope<Root extends CoValue> {
|
export class SubscriptionScope<Root extends CoValue> {
|
||||||
scopeID: string = `scope-${Math.random().toString(36).slice(2)}`;
|
scopeID: string = `scope-${Math.random().toString(36).slice(2)}`;
|
||||||
subscriber: Account;
|
subscriber: Account;
|
||||||
@@ -112,11 +48,7 @@ export class SubscriptionScope<Root extends CoValue> {
|
|||||||
subscriptionsScopes.set(root, this);
|
subscriptionsScopes.set(root, this);
|
||||||
|
|
||||||
this.subscriber = root._loadedAs;
|
this.subscriber = root._loadedAs;
|
||||||
|
this.onUpdate = onUpdate;
|
||||||
this.onUpdate = throttle((update) => {
|
|
||||||
console.log("onUpdate");
|
|
||||||
onUpdate(update);
|
|
||||||
}, 50);
|
|
||||||
this.rootEntry.rawUnsub = root._raw.core.subscribe(
|
this.rootEntry.rawUnsub = root._raw.core.subscribe(
|
||||||
(rawUpdate: RawCoValue | undefined) => {
|
(rawUpdate: RawCoValue | undefined) => {
|
||||||
if (!rawUpdate) return;
|
if (!rawUpdate) return;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
WasmCrypto,
|
WasmCrypto,
|
||||||
isControlledAccount,
|
isControlledAccount,
|
||||||
} from "../index.js";
|
} from "../index.js";
|
||||||
|
import { Schema } from "@effect/schema";
|
||||||
|
|
||||||
const Crypto = await WasmCrypto.create();
|
const Crypto = await WasmCrypto.create();
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ describe("Simple CoMap operations", async () => {
|
|||||||
_height = co.number;
|
_height = co.number;
|
||||||
birthday = co.encoded(Encoders.Date);
|
birthday = co.encoded(Encoders.Date);
|
||||||
name? = co.string;
|
name? = co.string;
|
||||||
|
nullable = co.encoded(Schema.NullOr(Schema.String));
|
||||||
|
|
||||||
get roughColor() {
|
get roughColor() {
|
||||||
return this.color + "ish";
|
return this.color + "ish";
|
||||||
@@ -39,6 +41,7 @@ describe("Simple CoMap operations", async () => {
|
|||||||
color: "red",
|
color: "red",
|
||||||
_height: 10,
|
_height: 10,
|
||||||
birthday: birthday,
|
birthday: birthday,
|
||||||
|
nullable: null,
|
||||||
},
|
},
|
||||||
{ owner: me },
|
{ owner: me },
|
||||||
);
|
);
|
||||||
@@ -49,7 +52,12 @@ describe("Simple CoMap operations", async () => {
|
|||||||
expect(map._height).toEqual(10);
|
expect(map._height).toEqual(10);
|
||||||
expect(map.birthday).toEqual(birthday);
|
expect(map.birthday).toEqual(birthday);
|
||||||
expect(map._raw.get("birthday")).toEqual(birthday.toISOString());
|
expect(map._raw.get("birthday")).toEqual(birthday.toISOString());
|
||||||
expect(Object.keys(map)).toEqual(["color", "_height", "birthday"]);
|
expect(Object.keys(map)).toEqual([
|
||||||
|
"color",
|
||||||
|
"_height",
|
||||||
|
"birthday",
|
||||||
|
"nullable",
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Construction with too many things provided", () => {
|
test("Construction with too many things provided", () => {
|
||||||
@@ -84,6 +92,9 @@ describe("Simple CoMap operations", async () => {
|
|||||||
expect(map._height).toEqual(20);
|
expect(map._height).toEqual(20);
|
||||||
expect(map._raw.get("_height")).toEqual(20);
|
expect(map._raw.get("_height")).toEqual(20);
|
||||||
|
|
||||||
|
map.nullable = "not null";
|
||||||
|
map.nullable = null;
|
||||||
|
|
||||||
map.name = "Secret name";
|
map.name = "Secret name";
|
||||||
expect(map.name).toEqual("Secret name");
|
expect(map.name).toEqual("Secret name");
|
||||||
map.name = undefined;
|
map.name = undefined;
|
||||||
@@ -253,11 +264,12 @@ describe("CoMap resolution", async () => {
|
|||||||
|
|
||||||
test("Loading and availability", async () => {
|
test("Loading and availability", async () => {
|
||||||
const { me, map } = await initNodeAndMap();
|
const { me, map } = await initNodeAndMap();
|
||||||
const [initialAsPeer, secondPeer] = await Effect.runPromise(connectedPeers(
|
const [initialAsPeer, secondPeer] = await Effect.runPromise(
|
||||||
"initial",
|
connectedPeers("initial", "second", {
|
||||||
"second",
|
peer1role: "server",
|
||||||
{ peer1role: "server", peer2role: "client" },
|
peer2role: "client",
|
||||||
));
|
}),
|
||||||
|
);
|
||||||
if (!isControlledAccount(me)) {
|
if (!isControlledAccount(me)) {
|
||||||
throw "me is not a controlled account";
|
throw "me is not a controlled account";
|
||||||
}
|
}
|
||||||
@@ -323,11 +335,12 @@ describe("CoMap resolution", async () => {
|
|||||||
test("Subscription & auto-resolution", async () => {
|
test("Subscription & auto-resolution", async () => {
|
||||||
const { me, map } = await initNodeAndMap();
|
const { me, map } = await initNodeAndMap();
|
||||||
|
|
||||||
const [initialAsPeer, secondAsPeer] = await Effect.runPromise(connectedPeers(
|
const [initialAsPeer, secondAsPeer] = await Effect.runPromise(
|
||||||
"initial",
|
connectedPeers("initial", "second", {
|
||||||
"second",
|
peer1role: "server",
|
||||||
{ peer1role: "server", peer2role: "client" },
|
peer2role: "client",
|
||||||
));
|
}),
|
||||||
|
);
|
||||||
if (!isControlledAccount(me)) {
|
if (!isControlledAccount(me)) {
|
||||||
throw "me is not a controlled account";
|
throw "me is not a controlled account";
|
||||||
}
|
}
|
||||||
|
|||||||
103
pnpm-lock.yaml
generated
103
pnpm-lock.yaml
generated
@@ -136,6 +136,109 @@ importers:
|
|||||||
specifier: ^5.0.10
|
specifier: ^5.0.10
|
||||||
version: 5.0.10(@types/node@20.10.5)
|
version: 5.0.10(@types/node@20.10.5)
|
||||||
|
|
||||||
|
examples/inspector:
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-checkbox':
|
||||||
|
specifier: ^1.0.4
|
||||||
|
version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||||
|
'@radix-ui/react-slot':
|
||||||
|
specifier: ^1.0.2
|
||||||
|
version: 1.0.2(@types/react@18.2.45)(react@18.2.0)
|
||||||
|
'@radix-ui/react-toast':
|
||||||
|
specifier: ^1.1.4
|
||||||
|
version: 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||||
|
'@types/qrcode':
|
||||||
|
specifier: ^1.5.1
|
||||||
|
version: 1.5.5
|
||||||
|
class-variance-authority:
|
||||||
|
specifier: ^0.7.0
|
||||||
|
version: 0.7.0
|
||||||
|
clsx:
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0
|
||||||
|
cojson:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/cojson
|
||||||
|
cojson-transport-ws:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/cojson-transport-ws
|
||||||
|
effect:
|
||||||
|
specifier: ^3.1.5
|
||||||
|
version: 3.2.1
|
||||||
|
hash-slash:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/hash-slash
|
||||||
|
lucide-react:
|
||||||
|
specifier: ^0.274.0
|
||||||
|
version: 0.274.0(react@18.2.0)
|
||||||
|
qrcode:
|
||||||
|
specifier: ^1.5.3
|
||||||
|
version: 1.5.3
|
||||||
|
react:
|
||||||
|
specifier: ^18.2.0
|
||||||
|
version: 18.2.0
|
||||||
|
react-dom:
|
||||||
|
specifier: ^18.2.0
|
||||||
|
version: 18.2.0(react@18.2.0)
|
||||||
|
react-router:
|
||||||
|
specifier: ^6.16.0
|
||||||
|
version: 6.21.0(react@18.2.0)
|
||||||
|
react-router-dom:
|
||||||
|
specifier: ^6.16.0
|
||||||
|
version: 6.21.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||||
|
react-use:
|
||||||
|
specifier: ^17.4.0
|
||||||
|
version: 17.4.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||||
|
tailwind-merge:
|
||||||
|
specifier: ^1.14.0
|
||||||
|
version: 1.14.0
|
||||||
|
tailwindcss-animate:
|
||||||
|
specifier: ^1.0.7
|
||||||
|
version: 1.0.7(tailwindcss@3.4.0(ts-node@10.9.2(@swc/core@1.3.101)(@types/node@20.10.5)(typescript@5.3.3)))
|
||||||
|
uniqolor:
|
||||||
|
specifier: ^1.1.0
|
||||||
|
version: 1.1.1
|
||||||
|
devDependencies:
|
||||||
|
'@types/react':
|
||||||
|
specifier: ^18.2.15
|
||||||
|
version: 18.2.45
|
||||||
|
'@types/react-dom':
|
||||||
|
specifier: ^18.2.7
|
||||||
|
version: 18.2.18
|
||||||
|
'@typescript-eslint/eslint-plugin':
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.15.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@typescript-eslint/parser':
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.15.0(eslint@8.56.0)(typescript@5.3.3)
|
||||||
|
'@vitejs/plugin-react-swc':
|
||||||
|
specifier: ^3.3.2
|
||||||
|
version: 3.5.0(vite@5.0.10(@types/node@20.10.5))
|
||||||
|
autoprefixer:
|
||||||
|
specifier: ^10.4.14
|
||||||
|
version: 10.4.16(postcss@8.4.32)
|
||||||
|
eslint:
|
||||||
|
specifier: ^8.45.0
|
||||||
|
version: 8.56.0
|
||||||
|
eslint-plugin-react-hooks:
|
||||||
|
specifier: ^4.6.0
|
||||||
|
version: 4.6.0(eslint@8.56.0)
|
||||||
|
eslint-plugin-react-refresh:
|
||||||
|
specifier: ^0.4.3
|
||||||
|
version: 0.4.5(eslint@8.56.0)
|
||||||
|
postcss:
|
||||||
|
specifier: ^8.4.27
|
||||||
|
version: 8.4.32
|
||||||
|
tailwindcss:
|
||||||
|
specifier: ^3.3.3
|
||||||
|
version: 3.4.0(ts-node@10.9.2(@swc/core@1.3.101)(@types/node@20.10.5)(typescript@5.3.3))
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.0.2
|
||||||
|
version: 5.3.3
|
||||||
|
vite:
|
||||||
|
specifier: ^5.0.10
|
||||||
|
version: 5.0.10(@types/node@20.10.5)
|
||||||
|
|
||||||
examples/pets:
|
examples/pets:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/react-checkbox':
|
'@radix-ui/react-checkbox':
|
||||||
|
|||||||
Reference in New Issue
Block a user