Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ pnpm build
hypergraph typesync
```


Any time you make changes to the schema, you will need to run the following commands:

```sh
Expand All @@ -46,6 +45,13 @@ cd apps/next-example
pnpm dev
```

To run the docs locally, you can run:

```sh
cd docs
pnpm start
```


## Upgrading Dependencies

Expand Down
19 changes: 19 additions & 0 deletions docs/docs/advanced/running-connect-and-sync-server-locally.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Running Connect and Sync Server Locally

To run the Connect and Sync Server locally, you need to get the Hypergraph repository:

```bash
git clone https://github.com/graphprotocol/hypergraph.git
cd hypergraph
pnpm install
```

```bash
cd apps/connect
pnpm dev
# in another tab
cd apps/server
pnpm dev
```

The Connect app is available at `http://localhost:5173` and the Sync Server is available at `http://localhost:3000`.
117 changes: 117 additions & 0 deletions docs/docs/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Authentication

## Geo Connect

The default and recommended way to authenticate is via Geo Connect. Geo Connect is dedicated application hosted by the GeoBrowser team. Through Geo Connect you can authenticate with your GeoBrowser account and use it to selectively delegate access to your private and public spaces.

If you create you application using TypeSync or use the [hypergraph-app-template](https://github.com/graphprotocol/hypergraph-app-template) the full authentication flow is already implemented for you.

In the connect app you can create spaces. In the near future you will be able to delete private spaces and you also will be able to give an app permissions to create new private and/or public spaces.

## Hypergraph API

```tsx
import { useHypergraphAuth } from "@graphprotocol/hypergraph-react";

function RouteComponent() {
const { authenticated, identity } = useHypergraphAuth();
}
```

- `authenticated` - a boolean indicating if the user is authenticated
- `identity` - the identity of the logged in user

## Authentication Flows with Geo Connect

### Signup coming from an App (without an existing Geo account)

1. User is opening App (Running App)
2. Clicks on "Sign in/Sign up with Geo"
- Redirect to Connect
3. Sign up for Connect -> Email + One-time Code
4. Connect: "Do you authorize this app (App ID, redirect URL)"
- Select spaces
- Click "Authorize" -> Redirect to App
5. You are logged into the app with your account

### Signup coming from an App (with an existing Geo account)

1. User is opening App (Running App)
2. Clicks on "Sign in/Sign up with Geo"
- Redirect to Connect
3. Login in the connect App -> Email + One-time Code
4. Connect: "Do you authorize this app (App ID, redirect URL)"
- Select spaces
- Click "Authorize" -> Redirect to App
5. You are logged into the app with your account

### Login coming from an App (user is logged out from app and connect)

1. User is opening App (Running App)
2. Clicks on "Sign in/Sign up with Geo"
- Redirect to Connect
3. Login in the connect App -> Email + One-time Code
4. Do you want to login with this app? (App ID, redirect URL)
- Select spaces (optional)
- Click "Authorize" -> Redirect to App
5. You are logged into the app with your account

### Login coming from an App (user is logged out from app and logged in to connect)

1. User is opening App (Running App)
2. Clicks on "Sign in/Sign up with Geo"
- Redirect to Connect
3. Do you want to login with this app? (App ID, redirect URL)
- Select spaces (optional)
- Click "Authorize" -> Redirect to App
4. You are logged into the app with your account

## Geo Connect API

### `redirectToConnect`

```tsx
import { useHypergraphApp } from "@graphprotocol/hypergraph-react";

function Login() {
const { redirectToConnect } = useHypergraphApp();
return (
<button
onClick={() => {
redirectToConnect({
storage: localStorage,
connectUrl: "https://hypergraph-connect.vercel.app/",
successUrl: `${window.location.origin}/authenticate-success`,
// your app id (any valid uuid)
appId: "93bb8907-085a-4a0e-83dd-62b0dc98e793",
redirectFn: (url: URL) => {
window.location.href = url.toString();
},
});
}}
>
Authenticate with Connect
</button>
);
}
```

### `processConnectAuthSuccess`

```tsx
import { useHypergraphApp } from "@graphprotocol/hypergraph-react";

function RouteComponent() {
const { ciphertext, nonce } = Route.useSearch(); // get the ciphertext and nonce from the URL
const { processConnectAuthSuccess } = useHypergraphApp();
const navigate = useNavigate();

useEffect(() => {
processConnectAuthSuccess({ storage: localStorage, ciphertext, nonce });
console.log("redirecting to /");
navigate({ to: "/", replace: true });
}, [ciphertext, nonce, processConnectAuthSuccess, navigate]);

return <div>Authenticating …</div>;
}
```
8 changes: 4 additions & 4 deletions docs/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ Find solutions in our Troubleshooting guide: [🛠 Troubleshooting](/docs/troubl

## Technical

### Which database do you use under the hood?
<!-- ### Which database do you use under the hood?

None. Updates are stored as **CRDT events** on the sync server and optionally mirrored to IPFS for redundancy. Public data is published as JSON-LD on-chain.
None. Updates are stored as **CRDT events** on the sync server and optionally mirrored to IPFS for redundancy. Public data is published as JSON-LD on-chain. -->

### Is Hypergraph open-source?

100 %. Apache-2.0 license. Contributions welcome!

### How big can a Space grow?
<!-- ### How big can a Space grow?

We tested 50 k events / 10 MB snapshots on consumer laptops. Planned optimizations include **document sharding** and delta compression.
We tested 50 k events / 10 MB snapshots on consumer laptops. Planned optimizations include **document sharding** and delta compression. -->

---

Expand Down
211 changes: 211 additions & 0 deletions docs/docs/filtering-query-results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# Filtering Query Results

The filter API allows you to filter the results of a query by property values and in the future also by relations.

Note: Filtering is not yet supported for public data.

## Filtering by property values

```tsx
export class Event extends Entity.Class<Event>("Event")({
name: Type.Text,
cancelled: Type.Checkbox,
}) {}

// inside the React component
const { data } = useQuery(Event, {
filter: {
cancelled: { is: false },
},
});
```

The filter API supports different filters for different property types and offers a logical `or` and `not` operator.

```tsx
// checkbox filter
{
is: true/false, // exact match
exists: true/false, // filter by existence of the property
}

// text filter
{
is: "text", // exact match
contains: "text",
startsWith: "text",
endsWith: "text",
exists: true/false, // filter by existence of the property
}

// number filter
{
is: 42,
lessThan: 42,
lessThanOrEqual: 42,
greaterThan: 42,
greaterThanOrEqual: 42,
exists: true/false, // filter by existence of the property
}

// point filter
{
is: [0, 42],
exists: true/false, // filter by existence of the property
}

// logical `not` for a string
{
not: {
is: "Jane Doe",
},
}

// logical `or` for a string
{
or: [
{ name: "Jane Doe" },
{ name: "John Doe" },
],
}
```

## Combining logical filters

```tsx

{
or: [
not: { name: "Jane Doe" },
not: { name: "John Doe" },
],
}
```

## Full examples

```tsx
// ever person except if their name is not Jane Doe or John Doe
const { data } = useQuery(Person, {
filter: {
or: [
not: { name: { is: "Jane Doe" } },
not: { name: { is: "John Doe" } },
],
},
});

// ever person that is 42, but their name is not Jane Doe or John Doe
const { data } = useQuery(Person, {
filter: {
age: {
equals: 42
},
or: [
not: { name: { is: "Jane Doe" } },
not: { name: { is: "John Doe" } },
],
not: {
or: [
{ name: { is: "Jane Doe" } },
{ name: { is: "John Doe" } },
],
},
},
});

// every person that is not 42 years old
const { data } = useQuery(Person, {
filter: {
age: {
not: { is: 42 },
},
},
});
```

## Relation filtering

### Filter on values of the to entity

```tsx
// schema
export class Todo extends Entity.Class<Todo2>('Todo')({
name: Type.Text,
checked: Type.Checkbox,
assignees: Type.Relation(User),
})
```

1 level filtering

```tsx
const { data } = useQuery(Person, {
filter: {
assignees: {
name: { is: "John" },
},
},
});
```

2 level filtering

```tsx
const { data } = useQuery(Person, {
filter: {
assignees: {
name: { is: "John" },
friends: {
age: { greaterThan: 60 },
},
},
includes: {
name: {},
description: {},
friends: {},
},
},
});
```

### Filter on the relation entity

```tsx
// schema
export class Todo extends Entity.Class<Todo2>('Todo')({
name: Type.Text,
checked: Type.Checkbox,
assignees: Type.Relation(User, {
entity: {
assignedAt: Type.DateTime,
}
}),
})
```

```tsx
const { data } = useQuery(Person, {
filter: {
assignees: {
_relation: {
entity: { assignedAt: { greaterThan: new Date("2025-01-01") } },
},
name: { is: "John" },
},
},
});
```

Note: To access the relation entity you need to use the `_relation` property.

```tsx
{
todo.assignees.map((assignee) => (
<span key={assignee._relation.id}>
{assignee._relation.entity.assignedAt}
{assignee.name}
</span>
));
}
```
Loading
Loading