diff --git a/apps/events/src/schema.ts b/apps/events/src/schema.ts index c54b0aa8..37ceb432 100644 --- a/apps/events/src/schema.ts +++ b/apps/events/src/schema.ts @@ -1,27 +1,27 @@ -import { Entity } from '@graphprotocol/hypergraph'; +import { Entity, Type } from '@graphprotocol/hypergraph'; export class User extends Entity.Class('User')({ - name: Entity.Text, + name: Type.Text, }) {} export class Todo extends Entity.Class('Todo')({ - name: Entity.Text, - completed: Entity.Checkbox, - assignees: Entity.Relation(User), + name: Type.Text, + completed: Type.Checkbox, + assignees: Type.Relation(User), }) {} export class Todo2 extends Entity.Class('Todo2')({ - name: Entity.Text, - checked: Entity.Checkbox, - assignees: Entity.Relation(User), - due: Entity.Date, - amount: Entity.Number, - point: Entity.Point, - website: Entity.Url, + name: Type.Text, + checked: Type.Checkbox, + assignees: Type.Relation(User), + due: Type.Date, + amount: Type.Number, + point: Type.Point, + website: Type.Url, }) {} export class NewsStory extends Entity.Class('NewsStory')({ - name: Entity.Text, - description: Entity.Text, - publishDate: Entity.Text, + name: Type.Text, + description: Type.Text, + publishDate: Type.Text, }) {} diff --git a/packages/hypergraph-react/src/internal/use-generate-create-ops.tsx b/packages/hypergraph-react/src/internal/use-generate-create-ops.tsx index 60e15922..8c4bbb66 100644 --- a/packages/hypergraph-react/src/internal/use-generate-create-ops.tsx +++ b/packages/hypergraph-react/src/internal/use-generate-create-ops.tsx @@ -1,5 +1,5 @@ import { Graph, Id, type PropertiesParam, type ValueType } from '@graphprotocol/grc-20'; -import { Entity } from '@graphprotocol/hypergraph'; +import { type Entity, Type } from '@graphprotocol/hypergraph'; import { useHypergraph } from '../HypergraphSpaceContext.js'; export function useGenerateCreateOps(type: S, enabled = true) { @@ -21,19 +21,19 @@ export function useGenerateCreateOps(type: for (const [key, value] of Object.entries(mappingEntry.properties || {})) { let valueType: ValueType = 'TEXT'; let serializedValue: string = properties[key]; - if (fields[key] === Entity.Checkbox) { + if (fields[key] === Type.Checkbox) { valueType = 'CHECKBOX'; serializedValue = properties[key] ? '1' : '0'; - } else if (fields[key] === Entity.Date) { + } else if (fields[key] === Type.Date) { valueType = 'TIME'; serializedValue = properties[key].toISOString(); - } else if (fields[key] === Entity.Point) { + } else if (fields[key] === Type.Point) { valueType = 'POINT'; serializedValue = properties[key].join(','); - } else if (fields[key] === Entity.Url) { + } else if (fields[key] === Type.Url) { valueType = 'URL'; serializedValue = properties[key].toString(); - } else if (fields[key] === Entity.Number) { + } else if (fields[key] === Type.Number) { valueType = 'NUMBER'; serializedValue = properties[key].toString(); } diff --git a/packages/hypergraph-react/src/internal/use-generate-update-ops.tsx b/packages/hypergraph-react/src/internal/use-generate-update-ops.tsx index 4f52df69..0ad33e14 100644 --- a/packages/hypergraph-react/src/internal/use-generate-update-ops.tsx +++ b/packages/hypergraph-react/src/internal/use-generate-update-ops.tsx @@ -1,5 +1,5 @@ import { Id, type Op, Relation, Triple, type Value } from '@graphprotocol/grc-20'; -import { Entity } from '@graphprotocol/hypergraph'; +import { type Entity, Type } from '@graphprotocol/hypergraph'; import { useHypergraph } from '../HypergraphSpaceContext.js'; import type { DiffEntry } from '../types.js'; @@ -31,30 +31,30 @@ export function useGenerateUpdateOps(type: const rawValue = propertyDiff.new; let value: Value; - if (type.fields[key] === Entity.Checkbox) { + if (type.fields[key] === Type.Checkbox) { value = { type: 'CHECKBOX', value: rawValue ? '1' : '0', }; - } else if (type.fields[key] === Entity.Point) { + } else if (type.fields[key] === Type.Point) { value = { type: 'POINT', // @ts-expect-error: must be an array of numbers value: rawValue.join(','), }; - } else if (type.fields[key] === Entity.Url) { + } else if (type.fields[key] === Type.Url) { value = { type: 'URL', // @ts-expect-error: must be a URL value: rawValue.toString(), }; - } else if (type.fields[key] === Entity.Date) { + } else if (type.fields[key] === Type.Date) { value = { type: 'TIME', // @ts-expect-error: must be a Date value: rawValue.toISOString(), }; - } else if (type.fields[key] === Entity.Number) { + } else if (type.fields[key] === Type.Number) { value = { type: 'NUMBER', // @ts-expect-error: must be a number diff --git a/packages/hypergraph-react/src/internal/use-query-public-geo.tsx b/packages/hypergraph-react/src/internal/use-query-public-geo.tsx index 3d4745d8..3ee44939 100644 --- a/packages/hypergraph-react/src/internal/use-query-public-geo.tsx +++ b/packages/hypergraph-react/src/internal/use-query-public-geo.tsx @@ -1,4 +1,4 @@ -import { Entity } from '@graphprotocol/hypergraph'; +import { type Entity, Type } from '@graphprotocol/hypergraph'; import { useQuery as useQueryTanstack } from '@tanstack/react-query'; import * as Either from 'effect/Either'; import * as Schema from 'effect/Schema'; @@ -113,15 +113,15 @@ export const parseResult = ( for (const [key, value] of Object.entries(mappingEntry?.properties ?? {})) { const property = queryEntityVersion.triples.nodes.find((a) => a.attributeId === value); if (property) { - if (type.fields[key] === Entity.Checkbox) { + if (type.fields[key] === Type.Checkbox) { rawEntity[key] = property.booleanValue; - } else if (type.fields[key] === Entity.Point) { + } else if (type.fields[key] === Type.Point) { rawEntity[key] = property.textValue; - } else if (type.fields[key] === Entity.Url) { + } else if (type.fields[key] === Type.Url) { rawEntity[key] = property.textValue; - } else if (type.fields[key] === Entity.Date) { + } else if (type.fields[key] === Type.Date) { rawEntity[key] = property.textValue; - } else if (type.fields[key] === Entity.Number) { + } else if (type.fields[key] === Type.Number) { rawEntity[key] = Number(property.textValue); } else { rawEntity[key] = property.textValue; diff --git a/packages/hypergraph-react/src/internal/use-query-public-kg.tsx b/packages/hypergraph-react/src/internal/use-query-public-kg.tsx index cdd331fa..d79a5180 100644 --- a/packages/hypergraph-react/src/internal/use-query-public-kg.tsx +++ b/packages/hypergraph-react/src/internal/use-query-public-kg.tsx @@ -1,4 +1,4 @@ -import { Entity } from '@graphprotocol/hypergraph'; +import { type Entity, Type } from '@graphprotocol/hypergraph'; import { useQuery as useQueryTanstack } from '@tanstack/react-query'; import * as Either from 'effect/Either'; import * as Schema from 'effect/Schema'; @@ -61,7 +61,7 @@ export function useQueryPublic(type: S, par for (const [key, value] of Object.entries(mappingEntry?.properties ?? {})) { const property = queryEntity.attributes.find((a) => a.attribute === value); if (property) { - if (type.fields[key] === Entity.Checkbox) { + if (type.fields[key] === Type.Checkbox) { rawEntity[key] = property.value === '1'; } else { rawEntity[key] = property.value; diff --git a/packages/hypergraph-react/src/use-query.tsx b/packages/hypergraph-react/src/use-query.tsx index dd7fef23..70948a10 100644 --- a/packages/hypergraph-react/src/use-query.tsx +++ b/packages/hypergraph-react/src/use-query.tsx @@ -1,4 +1,4 @@ -import { Entity, Utils } from '@graphprotocol/hypergraph'; +import { type Entity, Type, Utils } from '@graphprotocol/hypergraph'; import type * as Schema from 'effect/Schema'; import { useMemo } from 'react'; import { useHypergraph, useQueryLocal } from './HypergraphSpaceContext.js'; @@ -90,7 +90,7 @@ const getDiff = ( }; } } else { - if (field === Entity.Date) { + if (field === Type.Date) { if (entity[key].getTime() !== localEntity[key].getTime()) { diff[key] = { type: 'property', @@ -98,7 +98,7 @@ const getDiff = ( new: localEntity[key], }; } - } else if (field === Entity.Url) { + } else if (field === Type.Url) { if (entity[key].toString() !== localEntity[key].toString()) { diff[key] = { type: 'property', @@ -106,7 +106,7 @@ const getDiff = ( new: localEntity[key], }; } - } else if (field === Entity.Point) { + } else if (field === Type.Point) { if (entity[key].join(',') !== localEntity[key].join(',')) { diff[key] = { type: 'property', diff --git a/packages/hypergraph-react/test/HypergraphSpaceContext.test.tsx b/packages/hypergraph-react/test/HypergraphSpaceContext.test.tsx index bd0e2685..8dbf3f22 100644 --- a/packages/hypergraph-react/test/HypergraphSpaceContext.test.tsx +++ b/packages/hypergraph-react/test/HypergraphSpaceContext.test.tsx @@ -1,6 +1,6 @@ import { type AnyDocumentId, Repo } from '@automerge/automerge-repo'; import { RepoContext } from '@automerge/automerge-repo-react-hooks'; -import { Entity, Utils } from '@graphprotocol/hypergraph'; +import { Entity, Type, Utils } from '@graphprotocol/hypergraph'; import '@testing-library/jest-dom/vitest'; import { act, cleanup, renderHook, waitFor } from '@testing-library/react'; // biome-ignore lint/style/useImportType: @@ -21,17 +21,17 @@ afterEach(() => { describe('HypergraphSpaceContext', () => { class Person extends Entity.Class('Person')({ - name: Entity.Text, - age: Entity.Number, + name: Type.Text, + age: Type.Number, }) {} class User extends Entity.Class('User')({ - name: Entity.Text, - email: Entity.Text, + name: Type.Text, + email: Type.Text, }) {} class Event extends Entity.Class('Event')({ - name: Entity.Text, + name: Type.Text, }) {} const spaceId = '52gTkePWSoGdXmgZF3nRU'; diff --git a/packages/hypergraph/src/entity/entity.ts b/packages/hypergraph/src/entity/entity.ts index dff1f173..c772ed52 100644 --- a/packages/hypergraph/src/entity/entity.ts +++ b/packages/hypergraph/src/entity/entity.ts @@ -1,6 +1,5 @@ import * as Data from 'effect/Data'; -import * as Schema from 'effect/Schema'; -import type { AnyNoContext, EntityWithRelation } from './types.js'; +import type { AnyNoContext } from './types.js'; import * as VariantSchema from './variant-schema.js'; const { @@ -18,32 +17,10 @@ const { defaultVariant: 'select', }); -export { Class }; - -export const Text = Schema.String; -// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok -export const Number = Schema.Number; -export const Checkbox = Schema.Boolean; -// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok -export const Date = Schema.Date; -export const Url = Schema.URL; -export const Point = Schema.transform(Schema.String, Schema.Array(Number), { - strict: true, - decode: (str: string) => { - return str.split(',').map((n: string) => globalThis.Number(n)); - }, - encode: (points: readonly number[]) => points.join(','), -}); +export { Class, Field }; export class EntityNotFoundError extends Data.TaggedError('EntityNotFoundError')<{ id: string; type: AnyNoContext; cause?: unknown; }> {} - -export const Relation = (schema: S) => - Field({ - select: Schema.Array(schema) as unknown as Schema.Schema>>, - insert: Schema.optional(Schema.Array(Schema.String)), - update: Schema.Undefined, - }); diff --git a/packages/hypergraph/src/index.ts b/packages/hypergraph/src/index.ts index 45513bcd..a5c66486 100644 --- a/packages/hypergraph/src/index.ts +++ b/packages/hypergraph/src/index.ts @@ -1,8 +1,9 @@ export * as Entity from './entity/index.js'; export * as Identity from './identity/index.js'; +export * as Inboxes from './inboxes/index.js'; export * as Key from './key/index.js'; export * as Messages from './messages/index.js'; export * as SpaceEvents from './space-events/index.js'; -export * as Inboxes from './inboxes/index.js'; export * from './store.js'; +export * as Type from './type/type.js'; export * as Utils from './utils/index.js'; diff --git a/packages/hypergraph/src/type/type.ts b/packages/hypergraph/src/type/type.ts new file mode 100644 index 00000000..da8dfbe7 --- /dev/null +++ b/packages/hypergraph/src/type/type.ts @@ -0,0 +1,25 @@ +import * as Schema from 'effect/Schema'; +import { Field } from '../entity/entity.js'; +import type { AnyNoContext, EntityWithRelation } from '../entity/types.js'; + +export const Text = Schema.String; +// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok +export const Number = Schema.Number; +export const Checkbox = Schema.Boolean; +// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok +export const Date = Schema.Date; +export const Url = Schema.URL; +export const Point = Schema.transform(Schema.String, Schema.Array(Number), { + strict: true, + decode: (str: string) => { + return str.split(',').map((n: string) => globalThis.Number(n)); + }, + encode: (points: readonly number[]) => points.join(','), +}); + +export const Relation = (schema: S) => + Field({ + select: Schema.Array(schema) as unknown as Schema.Schema>>, + insert: Schema.optional(Schema.Array(Schema.String)), + update: Schema.Undefined, + }); diff --git a/packages/hypergraph/test/entity/entity.test.ts b/packages/hypergraph/test/entity/entity.test.ts index 8921a9af..c4b07580 100644 --- a/packages/hypergraph/test/entity/entity.test.ts +++ b/packages/hypergraph/test/entity/entity.test.ts @@ -3,25 +3,26 @@ import { Repo } from '@automerge/automerge-repo'; import { beforeEach, describe, expect, it } from 'vitest'; import * as Entity from '../../src/entity/index.js'; +import * as Type from '../../src/type/type.js'; import { idToAutomergeId } from '../../src/utils/automergeId.js'; describe('Entity', () => { class Person extends Entity.Class('Person')({ - name: Entity.Text, - age: Entity.Number, + name: Type.Text, + age: Type.Number, }) {} class User extends Entity.Class('User')({ - name: Entity.Text, - email: Entity.Text, + name: Type.Text, + email: Type.Text, }) {} class Badge extends Entity.Class('Badge')({ - name: Entity.Text, + name: Type.Text, }) {} class Event extends Entity.Class('Event')({ - name: Entity.Text, + name: Type.Text, }) {} const spaceId = '52gTkePWSoGdXmgZF3nRU'; diff --git a/packages/hypergraph/test/entity/findMany.test.ts b/packages/hypergraph/test/entity/findMany.test.ts index b08b91d6..12e2d375 100644 --- a/packages/hypergraph/test/entity/findMany.test.ts +++ b/packages/hypergraph/test/entity/findMany.test.ts @@ -3,20 +3,21 @@ import { Repo } from '@automerge/automerge-repo'; import { beforeEach, describe, expect, it } from 'vitest'; import * as Entity from '../../src/entity/index.js'; +import * as Type from '../../src/type/type.js'; import { idToAutomergeId } from '../../src/utils/automergeId.js'; describe('findMany with filters', () => { // Define entity classes for testing class Person extends Entity.Class('Person')({ - name: Entity.Text, - age: Entity.Number, - isActive: Entity.Checkbox, + name: Type.Text, + age: Type.Number, + isActive: Type.Checkbox, }) {} class Product extends Entity.Class('Product')({ - name: Entity.Text, - price: Entity.Number, - category: Entity.Text, + name: Type.Text, + price: Type.Number, + category: Type.Text, }) {} const spaceId = '52gTkePWSoGdXmgZF3nRU';