Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/events/src/components/users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { UserEntry } from './user-entry.js';
export const Users = () => {
const { data: users } = useQuery(User, { mode: 'private' });
const { ready: spaceReady } = useSpace({ mode: 'private' });
const createEntity = useCreateEntity(User, { space: '1c954768-7e14-4f0f-9396-0fe9dcd55fe8' });
const createEntity = useCreateEntity(User);
const [newUserName, setNewUserName] = useState('');

if (!spaceReady) {
Expand Down
11 changes: 5 additions & 6 deletions packages/hypergraph-react/src/HypergraphAppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ import {
useRef,
useState,
} from 'react';
import type { Address } from 'viem';
import type { Hex } from 'viem';
import type { Address, Hex } from 'viem';

const decodeResponseMessage = Schema.decodeUnknownEither(Messages.ResponseMessage);

Expand Down Expand Up @@ -362,10 +361,10 @@ export function HypergraphAppProvider({
});
const authorIdentity = await Identity.getVerifiedIdentity(update.accountAddress, syncServerUri);
if (authorIdentity.signaturePublicKey !== signer) {
console.error(
`Received invalid signature, recovered signer is ${signer},
expected ${authorIdentity.signaturePublicKey}`,
);
// console.error(
// `Received invalid signature, recovered signer is ${signer},
// expected ${authorIdentity.signaturePublicKey}`,
// );
// TODO bring back signature verfication
// return { valid: false, update: new Uint8Array([]) };
Comment on lines +364 to 369
Copy link

Copilot AI Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out logging can clutter the codebase. Either remove the dead code or re-enable the check with a proper logging strategy and a clear TODO note.

Suggested change
// console.error(
// `Received invalid signature, recovered signer is ${signer},
// expected ${authorIdentity.signaturePublicKey}`,
// );
// TODO bring back signature verfication
// return { valid: false, update: new Uint8Array([]) };
console.error(
`Received invalid signature, recovered signer is ${signer}, expected ${authorIdentity.signaturePublicKey}`
);
// TODO: Implement signature verification logic here to handle invalid signatures.
return { valid: false, update: new Uint8Array([]) };

Copilot uses AI. Check for mistakes.

}
Expand Down
59 changes: 45 additions & 14 deletions packages/hypergraph-react/src/HypergraphSpaceContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use client';

import type { AnyDocumentId } from '@automerge/automerge-repo';
import { useRepo } from '@automerge/automerge-repo-react-hooks';
import { Entity, Utils, store } from '@graphprotocol/hypergraph';
import { Entity, store } from '@graphprotocol/hypergraph';
import { useSelector } from '@xstate/store/react';
import * as Schema from 'effect/Schema';
import {
Expand Down Expand Up @@ -35,12 +33,13 @@
}

function useSubscribeToSpaceAndGetHandle({ spaceId, enabled }: { spaceId: string; enabled: boolean }) {
const repo = useRepo();
const handle = useMemo(() => {
const id = Utils.idToAutomergeId(spaceId) as AnyDocumentId;
const result = repo.findWithProgress<Entity.DocumentContent>(id);
return result.handle;
}, [spaceId, repo]);
const handle = useSelector(store, (state) => {
const space = state.context.spaces.find((space) => space.id === spaceId);
if (!space) {
return undefined;
}
return space.automergeDocHandle;
});

const { subscribeToSpace, isConnecting } = useHypergraphApp();
useEffect(() => {
Expand All @@ -61,43 +60,69 @@
const { space: spaceIdFromParams } = options ?? {};
const spaceId = spaceIdFromParams ?? spaceIdFromContext;
const handle = useSubscribeToSpaceAndGetHandle({ spaceId, enabled: options.mode === 'private' });
const ready = options.mode === 'public' ? true : handle.isReady();
const ready = options.mode === 'public' ? true : handle ? handle.isReady() : false;
const space = useSelector(store, (state) => state.context.spaces.find((space) => space.id === spaceId));
return { ready, name: space?.name, id: spaceId };
}

export function useCreateEntity<const S extends Entity.AnyNoContext>(type: S, options?: { space?: string }) {
const { space } = options ?? {};
const { space: spaceIdFromParams } = options ?? {};
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: space ?? spaceFromContext, enabled: true });
const spaceId = spaceIdFromParams ?? spaceFromContext;
const handle = useSubscribeToSpaceAndGetHandle({ spaceId, enabled: true });
if (!handle) {
return () => {
throw new Error('Space not found or not ready');

Check failure on line 75 in packages/hypergraph-react/src/HypergraphSpaceContext.tsx

View workflow job for this annotation

GitHub Actions / Install deps, build required package, typecheck, lint and test

test/HypergraphSpaceContext.test.tsx > HypergraphSpaceContext > useDeleteEntity > should be able to delete the created entity

Error: Space not found or not ready ❯ Object.current src/HypergraphSpaceContext.tsx:75:13 ❯ test/HypergraphSpaceContext.test.tsx:144:44 ❯ ../../node_modules/.pnpm/@testing-library[email protected]_@[email protected]_@[email protected]_@type_2e6fffc0caf50725d4db38bb5836f838/node_modules/@testing-library/react/dist/act-compat.js:48:24 ❯ process.env.NODE_ENV.exports.act ../../node_modules/.pnpm/[email protected]/node_modules/react/cjs/react.development.js:789:22 ❯ ../../node_modules/.pnpm/@testing-library[email protected]_@[email protected]_@[email protected]_@type_2e6fffc0caf50725d4db38bb5836f838/node_modules/@testing-library/react/dist/act-compat.js:47:25 ❯ test/HypergraphSpaceContext.test.tsx:143:7

Check failure on line 75 in packages/hypergraph-react/src/HypergraphSpaceContext.tsx

View workflow job for this annotation

GitHub Actions / Install deps, build required package, typecheck, lint and test

test/HypergraphSpaceContext.test.tsx > HypergraphSpaceContext > useUpdateEntity > should be able to update a created entity through the useUpdateEntity hook

Error: Space not found or not ready ❯ Object.current src/HypergraphSpaceContext.tsx:75:13 ❯ test/HypergraphSpaceContext.test.tsx:95:44 ❯ ../../node_modules/.pnpm/@testing-library[email protected]_@[email protected]_@[email protected]_@type_2e6fffc0caf50725d4db38bb5836f838/node_modules/@testing-library/react/dist/act-compat.js:48:24 ❯ process.env.NODE_ENV.exports.act ../../node_modules/.pnpm/[email protected]/node_modules/react/cjs/react.development.js:789:22 ❯ ../../node_modules/.pnpm/@testing-library[email protected]_@[email protected]_@[email protected]_@type_2e6fffc0caf50725d4db38bb5836f838/node_modules/@testing-library/react/dist/act-compat.js:47:25 ❯ test/HypergraphSpaceContext.test.tsx:94:7

Check failure on line 75 in packages/hypergraph-react/src/HypergraphSpaceContext.tsx

View workflow job for this annotation

GitHub Actions / Install deps, build required package, typecheck, lint and test

test/HypergraphSpaceContext.test.tsx > HypergraphSpaceContext > useCreateEntity > should be able to create an entity through the useCreateEntity Hook

Error: Space not found or not ready ❯ Object.current src/HypergraphSpaceContext.tsx:75:13 ❯ test/HypergraphSpaceContext.test.tsx:68:44 ❯ ../../node_modules/.pnpm/@testing-library[email protected]_@[email protected]_@[email protected]_@type_2e6fffc0caf50725d4db38bb5836f838/node_modules/@testing-library/react/dist/act-compat.js:48:24 ❯ process.env.NODE_ENV.exports.act ../../node_modules/.pnpm/[email protected]/node_modules/react/cjs/react.development.js:789:22 ❯ ../../node_modules/.pnpm/@testing-library[email protected]_@[email protected]_@[email protected]_@type_2e6fffc0caf50725d4db38bb5836f838/node_modules/@testing-library/react/dist/act-compat.js:47:25 ❯ test/HypergraphSpaceContext.test.tsx:67:7
};
}
return Entity.create(handle, type);
}

export function useUpdateEntity<const S extends Entity.AnyNoContext>(type: S, options?: { space?: string }) {
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const { space } = options ?? {};
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: space ?? spaceFromContext, enabled: true });
if (!handle) {
return () => {
throw new Error('Space not found or not ready');
};
}
return Entity.update(handle, type);
}

export function useDeleteEntity(options?: { space?: string }) {
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const { space } = options ?? {};
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: space ?? spaceFromContext, enabled: true });
if (!handle) {
return () => {
throw new Error('Space not found or not ready');
};
}
return Entity.markAsDeleted(handle);
}

export function useRemoveRelation(options?: { space?: string }) {
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const { space } = options ?? {};
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: space ?? spaceFromContext, enabled: true });
if (!handle) {
return () => {
throw new Error('Space not found or not ready');
};
}
return Entity.removeRelation(handle);
}

export function useHardDeleteEntity(options?: { space?: string }) {
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const { space } = options ?? {};
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: space ?? spaceFromContext, enabled: true });
if (!handle) {
return () => {
throw new Error('Space not found or not ready');
};
}
return Entity.delete(handle);
}

Expand All @@ -117,11 +142,11 @@
});
const { space: spaceFromContext } = useHypergraphSpaceInternal();
const handle = useSubscribeToSpaceAndGetHandle({ spaceId: spaceFromParams ?? spaceFromContext, enabled: true });
const handleIsReady = handle.isReady();
const handleIsReady = handle ? handle.isReady() : false;

// biome-ignore lint/correctness/useExhaustiveDependencies: allow to change filter and include
useLayoutEffect(() => {
if (enabled && handleIsReady) {
if (enabled && handle && handleIsReady) {
const subscription = Entity.subscribeToFindMany(handle, type, filter, include);
subscriptionRef.current.subscribe = subscription.subscribe;
subscriptionRef.current.getEntities = subscription.getEntities;
Expand Down Expand Up @@ -163,6 +188,9 @@
const equals = Schema.equivalence(type);

const subscribe = (callback: () => void) => {
if (!handle) {
return () => {};
}
const handleChange = () => {
callback();
};
Expand All @@ -181,6 +209,9 @@
};

return useSyncExternalStore(subscribe, () => {
if (!handle) {
return prevEntityRef.current;
}
const doc = handle.doc();
if (doc === undefined) {
return prevEntityRef.current;
Expand Down
Loading