diff --git a/packages/graph-framework-identity/package.json b/packages/graph-framework-identity/package.json
new file mode 100644
index 00000000..6869f68d
--- /dev/null
+++ b/packages/graph-framework-identity/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "graph-framework-identity",
+ "version": "0.0.1",
+ "description": "",
+ "type": "module",
+ "scripts": {
+ "test": "vitest run --typecheck",
+ "ts:check": "tsc --noEmit",
+ "lint": "echo 'No linting configured'"
+ },
+ "exports": {
+ ".": {
+ "default": "./src/index.js"
+ }
+ },
+ "peerDependencies": {
+ "@effect/schema": "^0.74"
+ },
+ "devDependencies": {
+ "@effect/schema": "^0.74.1",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.1"
+ },
+ "dependencies": {
+ "uuid": "^10.0.0",
+ "graph-framework-utils": "workspace:*"
+ }
+}
diff --git a/packages/graph-framework-identity/src/create-identity.test.ts b/packages/graph-framework-identity/src/create-identity.test.ts
new file mode 100644
index 00000000..11d1fbe9
--- /dev/null
+++ b/packages/graph-framework-identity/src/create-identity.test.ts
@@ -0,0 +1,6 @@
+import { expect, it } from "vitest";
+import { createIdentity } from "./create-identity.js";
+
+it.skip("should generate an identity", () => {
+ expect(createIdentity()).toEqual({});
+});
diff --git a/packages/graph-framework/src/identity/create-identity.ts b/packages/graph-framework-identity/src/create-identity.ts
similarity index 100%
rename from packages/graph-framework/src/identity/create-identity.ts
rename to packages/graph-framework-identity/src/create-identity.ts
diff --git a/packages/graph-framework-identity/src/index.tsx b/packages/graph-framework-identity/src/index.tsx
new file mode 100644
index 00000000..b26e0952
--- /dev/null
+++ b/packages/graph-framework-identity/src/index.tsx
@@ -0,0 +1,2 @@
+export * from "./create-identity.js";
+export * from "./restore-identity.js";
diff --git a/packages/graph-framework/src/identity/restore-identity.ts b/packages/graph-framework-identity/src/restore-identity.ts
similarity index 100%
rename from packages/graph-framework/src/identity/restore-identity.ts
rename to packages/graph-framework-identity/src/restore-identity.ts
diff --git a/packages/graph-framework-identity/tsconfig.json b/packages/graph-framework-identity/tsconfig.json
new file mode 100644
index 00000000..062fb3da
--- /dev/null
+++ b/packages/graph-framework-identity/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "esModuleInterop": true,
+ "sourceMap": true,
+ "declarationMap": true,
+ "declaration": true,
+ "strictNullChecks": true,
+ "incremental": true,
+ "composite": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "noErrorTruncation": true,
+ "isolatedModules": true,
+ "target": "ESNext",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "jsx": "react-jsx"
+ },
+ "include": ["./src"]
+}
diff --git a/packages/graph-framework-identity/vite.config.js b/packages/graph-framework-identity/vite.config.js
new file mode 100644
index 00000000..290663c3
--- /dev/null
+++ b/packages/graph-framework-identity/vite.config.js
@@ -0,0 +1,4 @@
+import { defineConfig } from "vite";
+
+// https://vitejs.dev/config/
+export default defineConfig({});
diff --git a/packages/graph-framework-schema/package.json b/packages/graph-framework-schema/package.json
new file mode 100644
index 00000000..814b2484
--- /dev/null
+++ b/packages/graph-framework-schema/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "graph-framework-schema",
+ "version": "0.0.1",
+ "description": "",
+ "type": "module",
+ "scripts": {
+ "test": "vitest run --typecheck",
+ "ts:check": "tsc --noEmit",
+ "lint": "echo 'No linting configured'"
+ },
+ "exports": {
+ ".": {
+ "default": "./src/index.js"
+ }
+ },
+ "peerDependencies": {
+ "@automerge/automerge": "^2",
+ "@automerge/automerge-repo": "^1",
+ "@automerge/automerge-repo-react-hooks": "^1",
+ "@effect/schema": "^0.74",
+ "react": "^18"
+ },
+ "devDependencies": {
+ "@automerge/automerge": "^2.2.8",
+ "@automerge/automerge-repo": "^1.2.1",
+ "@automerge/automerge-repo-react-hooks": "^1.2.1",
+ "@effect/schema": "^0.74.1",
+ "@testing-library/jest-dom": "^6.5.0",
+ "@testing-library/react": "^16.0.1",
+ "@types/react": "^18.3.7",
+ "@types/uuid": "^10.0.0",
+ "@vitejs/plugin-react": "^4.3.2",
+ "jsdom": "^25.0.1",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.1"
+ },
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "graph-framework-utils": "workspace:*"
+ }
+}
diff --git a/packages/graph-framework/src/context.tsx b/packages/graph-framework-schema/src/context.tsx
similarity index 100%
rename from packages/graph-framework/src/context.tsx
rename to packages/graph-framework-schema/src/context.tsx
diff --git a/packages/graph-framework-schema/src/index.test.tsx b/packages/graph-framework-schema/src/index.test.tsx
new file mode 100644
index 00000000..8cd52cc9
--- /dev/null
+++ b/packages/graph-framework-schema/src/index.test.tsx
@@ -0,0 +1,290 @@
+import "@testing-library/jest-dom/vitest";
+import { act, cleanup, renderHook } from "@testing-library/react";
+import React from "react";
+import {
+ afterEach,
+ beforeEach,
+ describe,
+ expect,
+ expectTypeOf,
+ it,
+} from "vitest";
+import { createFunctions, repo, type } from "./context.js";
+
+afterEach(() => {
+ cleanup();
+});
+
+describe("Library Tests", () => {
+ const schema = {
+ types: {
+ Person: {
+ name: type.Text,
+ age: type.Number,
+ badges: type.Relation({
+ types: ["Badge"] as const,
+ cardinality: "many",
+ }),
+ },
+ User: {
+ name: type.Text,
+ email: type.Text,
+ },
+ Badge: {
+ name: type.Text,
+ },
+ Event: {
+ name: type.Text,
+ participants: type.Relation({
+ types: ["Person"] as const,
+ cardinality: "many",
+ }),
+ author: type.Relation({
+ types: ["User", "Person"] as const,
+ cardinality: "one",
+ }),
+ },
+ },
+ };
+
+ // Create functions from the schema
+ const {
+ useCreateEntity,
+ useDeleteEntity,
+ useQuery,
+ SpaceProvider,
+ createDocumentId,
+ } = createFunctions(schema);
+
+ let repoResult = repo.create();
+ let wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
+ );
+
+ beforeEach(() => {
+ repoResult = repo.create();
+ wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
+ );
+ });
+
+ it("should create one entity successfully", () => {
+ expect([1]).toHaveLength(1);
+
+ const { result: createResult } = renderHook(() => useCreateEntity(), {
+ wrapper,
+ });
+
+ const { result: queryResult } = renderHook(
+ () => useQuery({ types: ["Event"] }),
+ { wrapper }
+ );
+
+ act(() => {
+ createResult.current(["Event"], {
+ name: "Conference",
+ participants: [
+ {
+ name: "Alice",
+ age: 30,
+ badges: [{ name: "Speaker" }],
+ },
+ {
+ name: "Bob",
+ age: 25,
+ badges: [{ name: "Attendee" }],
+ },
+ ],
+ author: {
+ name: "Charlie",
+ email: "charlie@example.com",
+ age: 35,
+ badges: [{ name: "VIP" }],
+ },
+ });
+ });
+
+ const events = queryResult.current;
+ expect(events).toHaveLength(1);
+ });
+
+ it("should create and query entities with relations", () => {
+ const { result: createResult } = renderHook(() => useCreateEntity(), {
+ wrapper,
+ });
+
+ const { result: queryResult } = renderHook(
+ () => useQuery({ types: ["Event"] }),
+ { wrapper }
+ );
+
+ act(() => {
+ createResult.current(["Event"], {
+ name: "Conference",
+ participants: [
+ {
+ name: "Alice",
+ age: 30,
+ badges: [{ name: "Speaker" }],
+ },
+ {
+ name: "Bob",
+ age: 25,
+ badges: [{ name: "Attendee" }],
+ },
+ ],
+ author: {
+ name: "Charlie",
+ email: "charlie@example.com",
+ age: 35,
+ badges: [{ name: "VIP" }],
+ },
+ });
+ });
+
+ // Wait for the hook to update
+ const events = queryResult.current;
+ expect(events).toHaveLength(1);
+
+ const event = events[0];
+ if (!event) {
+ throw new Error("Event not found");
+ }
+ expect(event.id).toBeTypeOf("string");
+ expect(event.types).toStrictEqual(["Event"]);
+ expect(event.name).toBe("Conference");
+
+ // Check participants
+ expect(event.participants).toHaveLength(2);
+ if (
+ !event.participants[0] ||
+ !event.participants[1] ||
+ !event.participants[0].badges[0] ||
+ !event.participants[1].badges[0]
+ ) {
+ throw new Error("Participants not found");
+ }
+
+ expect(event.participants[0].id).toBeTypeOf("string");
+ expect(event.participants[0].types).toStrictEqual(["Person"]);
+ expect(event.participants[0].name).toBe("Alice");
+ expect(event.participants[0].age).toBe(30);
+ expect(event.participants[0].badges).toHaveLength(1);
+ expect(event.participants[0].badges[0].name).toBe("Speaker");
+
+ expect(event.participants[1].id).toBeTypeOf("string");
+ expect(event.participants[1].types).toStrictEqual(["Person"]);
+ expect(event.participants[1].name).toBe("Bob");
+ expect(event.participants[1].age).toBe(25);
+ expect(event.participants[1].badges).toHaveLength(1);
+ expect(event.participants[1].badges[0].name).toBe("Attendee");
+
+ // Check author
+ expect(event.author.id).toBeTypeOf("string");
+ expect(event.author.types).toStrictEqual(["User", "Person"]);
+ expect(event.author.name).toBe("Charlie");
+ expect(event.author.email).toBe("charlie@example.com");
+
+ expectTypeOf(event).toMatchTypeOf<{
+ id: string;
+ types: string[];
+ name: string;
+ participants: {
+ id: string;
+ types: string[];
+ name: string;
+ age: number;
+ badges: {
+ id: string;
+ types: string[];
+ name: string;
+ }[];
+ }[];
+ author: {
+ id: string;
+ types: string[];
+ name: string;
+ age: number;
+ email: string;
+ badges: {
+ id: string;
+ types: string[];
+ name: string;
+ }[];
+ };
+ }>();
+ });
+
+ it("should create entities with nested relations and query them", () => {
+ const { result: createResult } = renderHook(() => useCreateEntity(), {
+ wrapper,
+ });
+
+ const { result: queryResult } = renderHook(
+ () => useQuery({ types: ["Person"] }),
+ { wrapper }
+ );
+
+ act(() => {
+ createResult.current(["Person"], {
+ name: "Dave",
+ age: 40,
+ badges: [{ name: "VIP" }, { name: "Contributor" }],
+ });
+ });
+
+ // Wait for the hook to update
+ const people = queryResult.current;
+ expect(people).toHaveLength(1);
+
+ const person = people[0];
+ if (!person) {
+ throw new Error("Person not found");
+ }
+ expect(person.name).toBe("Dave");
+ expect(person.age).toBe(40);
+ expect(person.badges).toHaveLength(2);
+ if (!person.badges[0] || !person.badges[1]) {
+ throw new Error("Badges not found");
+ }
+ expect(person.badges[0].name).toBe("VIP");
+ expect(person.badges[1].name).toBe("Contributor");
+ });
+
+ it("should delete an entity", () => {
+ const { result: createResult } = renderHook(() => useCreateEntity(), {
+ wrapper,
+ });
+
+ const { result: deleteResult } = renderHook(() => useDeleteEntity(), {
+ wrapper,
+ });
+
+ const { result: queryResult } = renderHook(
+ () => useQuery({ types: ["Badge"] }),
+ { wrapper }
+ );
+
+ let badgeId: string | undefined;
+
+ act(() => {
+ createResult.current(["Badge"], { name: "Exclusive" });
+ });
+
+ act(() => {
+ const badges = queryResult.current;
+ expect(badges).toHaveLength(1);
+ badgeId = badges[0]?.id;
+ });
+
+ act(() => {
+ const success = deleteResult.current(badgeId!);
+ expect(success).toBe(true);
+ });
+
+ act(() => {
+ const badgesAfterDelete = queryResult.current;
+ expect(badgesAfterDelete).toHaveLength(0);
+ });
+ });
+});
diff --git a/packages/graph-framework-schema/src/index.tsx b/packages/graph-framework-schema/src/index.tsx
new file mode 100644
index 00000000..45f75471
--- /dev/null
+++ b/packages/graph-framework-schema/src/index.tsx
@@ -0,0 +1 @@
+export * from "./context.js";
diff --git a/packages/graph-framework-schema/tsconfig.json b/packages/graph-framework-schema/tsconfig.json
new file mode 100644
index 00000000..062fb3da
--- /dev/null
+++ b/packages/graph-framework-schema/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "esModuleInterop": true,
+ "sourceMap": true,
+ "declarationMap": true,
+ "declaration": true,
+ "strictNullChecks": true,
+ "incremental": true,
+ "composite": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "noErrorTruncation": true,
+ "isolatedModules": true,
+ "target": "ESNext",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "jsx": "react-jsx"
+ },
+ "include": ["./src"]
+}
diff --git a/packages/graph-framework-schema/vite.config.js b/packages/graph-framework-schema/vite.config.js
new file mode 100644
index 00000000..b4f8c666
--- /dev/null
+++ b/packages/graph-framework-schema/vite.config.js
@@ -0,0 +1,10 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ environment: "jsdom",
+ },
+});
diff --git a/packages/graph-framework-space-events/package.json b/packages/graph-framework-space-events/package.json
new file mode 100644
index 00000000..81eaefe8
--- /dev/null
+++ b/packages/graph-framework-space-events/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "graph-framework-space-events",
+ "version": "0.0.1",
+ "description": "",
+ "type": "module",
+ "scripts": {
+ "test": "vitest run --typecheck",
+ "ts:check": "tsc --noEmit",
+ "lint": "echo 'No linting configured'"
+ },
+ "exports": {
+ ".": {
+ "default": "./src/index.js"
+ }
+ },
+ "peerDependencies": {
+ "@effect/schema": "^0.74"
+ },
+ "devDependencies": {
+ "@effect/schema": "^0.74.1",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.1"
+ },
+ "dependencies": {
+ "uuid": "^10.0.0",
+ "graph-framework-utils": "workspace:*"
+ }
+}
diff --git a/packages/graph-framework/src/space-events/apply-event.ts b/packages/graph-framework-space-events/src/apply-event.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/apply-event.ts
rename to packages/graph-framework-space-events/src/apply-event.ts
diff --git a/packages/graph-framework/src/space-events/create-invitation.test.ts b/packages/graph-framework-space-events/src/create-invitation.test.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/create-invitation.test.ts
rename to packages/graph-framework-space-events/src/create-invitation.test.ts
diff --git a/packages/graph-framework/src/space-events/create-invitation.ts b/packages/graph-framework-space-events/src/create-invitation.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/create-invitation.ts
rename to packages/graph-framework-space-events/src/create-invitation.ts
diff --git a/packages/graph-framework/src/space-events/create-space.test.ts b/packages/graph-framework-space-events/src/create-space.test.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/create-space.test.ts
rename to packages/graph-framework-space-events/src/create-space.test.ts
diff --git a/packages/graph-framework/src/space-events/create-space.ts b/packages/graph-framework-space-events/src/create-space.ts
similarity index 91%
rename from packages/graph-framework/src/space-events/create-space.ts
rename to packages/graph-framework-space-events/src/create-space.ts
index f3ed5039..318fa5f1 100644
--- a/packages/graph-framework/src/space-events/create-space.ts
+++ b/packages/graph-framework-space-events/src/create-space.ts
@@ -1,4 +1,4 @@
-import { generateId } from "../generateId.js";
+import { generateId } from "graph-framework-utils";
import { Author, SpaceEvent } from "./types.js";
type Params = {
diff --git a/packages/graph-framework/src/space-events/delete-space.test.ts b/packages/graph-framework-space-events/src/delete-space.test.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/delete-space.test.ts
rename to packages/graph-framework-space-events/src/delete-space.test.ts
diff --git a/packages/graph-framework/src/space-events/delete-space.ts b/packages/graph-framework-space-events/src/delete-space.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/delete-space.ts
rename to packages/graph-framework-space-events/src/delete-space.ts
diff --git a/packages/graph-framework-space-events/src/index.ts b/packages/graph-framework-space-events/src/index.ts
new file mode 100644
index 00000000..7ccc70b9
--- /dev/null
+++ b/packages/graph-framework-space-events/src/index.ts
@@ -0,0 +1,5 @@
+export * from "./apply-event.js";
+export * from "./create-invitation.js";
+export * from "./create-space.js";
+export * from "./delete-space.js";
+export * from "./types.js";
diff --git a/packages/graph-framework/src/space-events/types.ts b/packages/graph-framework-space-events/src/types.ts
similarity index 100%
rename from packages/graph-framework/src/space-events/types.ts
rename to packages/graph-framework-space-events/src/types.ts
diff --git a/packages/graph-framework-space-events/tsconfig.json b/packages/graph-framework-space-events/tsconfig.json
new file mode 100644
index 00000000..062fb3da
--- /dev/null
+++ b/packages/graph-framework-space-events/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "esModuleInterop": true,
+ "sourceMap": true,
+ "declarationMap": true,
+ "declaration": true,
+ "strictNullChecks": true,
+ "incremental": true,
+ "composite": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "noErrorTruncation": true,
+ "isolatedModules": true,
+ "target": "ESNext",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "jsx": "react-jsx"
+ },
+ "include": ["./src"]
+}
diff --git a/packages/graph-framework-space-events/vite.config.js b/packages/graph-framework-space-events/vite.config.js
new file mode 100644
index 00000000..290663c3
--- /dev/null
+++ b/packages/graph-framework-space-events/vite.config.js
@@ -0,0 +1,4 @@
+import { defineConfig } from "vite";
+
+// https://vitejs.dev/config/
+export default defineConfig({});
diff --git a/packages/graph-framework-utils/package.json b/packages/graph-framework-utils/package.json
new file mode 100644
index 00000000..79f80967
--- /dev/null
+++ b/packages/graph-framework-utils/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "graph-framework-utils",
+ "version": "0.0.1",
+ "description": "",
+ "type": "module",
+ "scripts": {
+ "test": "vitest run --typecheck",
+ "ts:check": "tsc --noEmit",
+ "lint": "echo 'No linting configured'"
+ },
+ "exports": {
+ ".": {
+ "default": "./src/index.js"
+ }
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.7",
+ "@types/uuid": "^10.0.0",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.1"
+ },
+ "dependencies": {
+ "uuid": "^10.0.0"
+ }
+}
diff --git a/packages/graph-framework-utils/src/generateId.test.ts b/packages/graph-framework-utils/src/generateId.test.ts
new file mode 100644
index 00000000..9f464cff
--- /dev/null
+++ b/packages/graph-framework-utils/src/generateId.test.ts
@@ -0,0 +1,10 @@
+import { expect, it } from "vitest";
+import { generateId } from "./generateId.js";
+
+it("should generate an id", () => {
+ expect(generateId()).toBeTypeOf("string");
+});
+
+it.skip("should have a length of 22 characters", () => {
+ expect(generateId()).toHaveLength(22);
+});
diff --git a/packages/graph-framework/src/generateId.ts b/packages/graph-framework-utils/src/generateId.ts
similarity index 100%
rename from packages/graph-framework/src/generateId.ts
rename to packages/graph-framework-utils/src/generateId.ts
diff --git a/packages/graph-framework-utils/src/index.tsx b/packages/graph-framework-utils/src/index.tsx
new file mode 100644
index 00000000..841ce577
--- /dev/null
+++ b/packages/graph-framework-utils/src/index.tsx
@@ -0,0 +1 @@
+export * from "./generateId.js";
diff --git a/packages/graph-framework-utils/tsconfig.json b/packages/graph-framework-utils/tsconfig.json
new file mode 100644
index 00000000..062fb3da
--- /dev/null
+++ b/packages/graph-framework-utils/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "esModuleInterop": true,
+ "sourceMap": true,
+ "declarationMap": true,
+ "declaration": true,
+ "strictNullChecks": true,
+ "incremental": true,
+ "composite": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "noErrorTruncation": true,
+ "isolatedModules": true,
+ "target": "ESNext",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "jsx": "react-jsx"
+ },
+ "include": ["./src"]
+}
diff --git a/packages/graph-framework-utils/vite.config.js b/packages/graph-framework-utils/vite.config.js
new file mode 100644
index 00000000..290663c3
--- /dev/null
+++ b/packages/graph-framework-utils/vite.config.js
@@ -0,0 +1,4 @@
+import { defineConfig } from "vite";
+
+// https://vitejs.dev/config/
+export default defineConfig({});
diff --git a/packages/graph-framework/package.json b/packages/graph-framework/package.json
index f98a34f1..51a48fce 100644
--- a/packages/graph-framework/package.json
+++ b/packages/graph-framework/package.json
@@ -32,11 +32,12 @@
"@vitejs/plugin-react": "^4.3.2",
"jsdom": "^25.0.1",
"vite": "^5.4.8",
- "vitest": "^2.1.1",
- "zod": "^3.23.8"
+ "vitest": "^2.1.1"
},
"dependencies": {
- "fast-deep-equal": "^3.1.3",
- "uuid": "^10.0.0"
+ "graph-framework-utils": "workspace:*",
+ "graph-framework-space-events": "workspace:*",
+ "graph-framework-identity": "workspace:*",
+ "graph-framework-schema": "workspace:*"
}
}
diff --git a/packages/graph-framework/src/index.test.tsx b/packages/graph-framework/src/index.test.tsx
index 8cd52cc9..9a5838a7 100644
--- a/packages/graph-framework/src/index.test.tsx
+++ b/packages/graph-framework/src/index.test.tsx
@@ -1,290 +1,5 @@
-import "@testing-library/jest-dom/vitest";
-import { act, cleanup, renderHook } from "@testing-library/react";
-import React from "react";
-import {
- afterEach,
- beforeEach,
- describe,
- expect,
- expectTypeOf,
- it,
-} from "vitest";
-import { createFunctions, repo, type } from "./context.js";
+import { expect, it } from "vitest";
-afterEach(() => {
- cleanup();
-});
-
-describe("Library Tests", () => {
- const schema = {
- types: {
- Person: {
- name: type.Text,
- age: type.Number,
- badges: type.Relation({
- types: ["Badge"] as const,
- cardinality: "many",
- }),
- },
- User: {
- name: type.Text,
- email: type.Text,
- },
- Badge: {
- name: type.Text,
- },
- Event: {
- name: type.Text,
- participants: type.Relation({
- types: ["Person"] as const,
- cardinality: "many",
- }),
- author: type.Relation({
- types: ["User", "Person"] as const,
- cardinality: "one",
- }),
- },
- },
- };
-
- // Create functions from the schema
- const {
- useCreateEntity,
- useDeleteEntity,
- useQuery,
- SpaceProvider,
- createDocumentId,
- } = createFunctions(schema);
-
- let repoResult = repo.create();
- let wrapper = ({ children }: { children: React.ReactNode }) => (
- {children}
- );
-
- beforeEach(() => {
- repoResult = repo.create();
- wrapper = ({ children }: { children: React.ReactNode }) => (
- {children}
- );
- });
-
- it("should create one entity successfully", () => {
- expect([1]).toHaveLength(1);
-
- const { result: createResult } = renderHook(() => useCreateEntity(), {
- wrapper,
- });
-
- const { result: queryResult } = renderHook(
- () => useQuery({ types: ["Event"] }),
- { wrapper }
- );
-
- act(() => {
- createResult.current(["Event"], {
- name: "Conference",
- participants: [
- {
- name: "Alice",
- age: 30,
- badges: [{ name: "Speaker" }],
- },
- {
- name: "Bob",
- age: 25,
- badges: [{ name: "Attendee" }],
- },
- ],
- author: {
- name: "Charlie",
- email: "charlie@example.com",
- age: 35,
- badges: [{ name: "VIP" }],
- },
- });
- });
-
- const events = queryResult.current;
- expect(events).toHaveLength(1);
- });
-
- it("should create and query entities with relations", () => {
- const { result: createResult } = renderHook(() => useCreateEntity(), {
- wrapper,
- });
-
- const { result: queryResult } = renderHook(
- () => useQuery({ types: ["Event"] }),
- { wrapper }
- );
-
- act(() => {
- createResult.current(["Event"], {
- name: "Conference",
- participants: [
- {
- name: "Alice",
- age: 30,
- badges: [{ name: "Speaker" }],
- },
- {
- name: "Bob",
- age: 25,
- badges: [{ name: "Attendee" }],
- },
- ],
- author: {
- name: "Charlie",
- email: "charlie@example.com",
- age: 35,
- badges: [{ name: "VIP" }],
- },
- });
- });
-
- // Wait for the hook to update
- const events = queryResult.current;
- expect(events).toHaveLength(1);
-
- const event = events[0];
- if (!event) {
- throw new Error("Event not found");
- }
- expect(event.id).toBeTypeOf("string");
- expect(event.types).toStrictEqual(["Event"]);
- expect(event.name).toBe("Conference");
-
- // Check participants
- expect(event.participants).toHaveLength(2);
- if (
- !event.participants[0] ||
- !event.participants[1] ||
- !event.participants[0].badges[0] ||
- !event.participants[1].badges[0]
- ) {
- throw new Error("Participants not found");
- }
-
- expect(event.participants[0].id).toBeTypeOf("string");
- expect(event.participants[0].types).toStrictEqual(["Person"]);
- expect(event.participants[0].name).toBe("Alice");
- expect(event.participants[0].age).toBe(30);
- expect(event.participants[0].badges).toHaveLength(1);
- expect(event.participants[0].badges[0].name).toBe("Speaker");
-
- expect(event.participants[1].id).toBeTypeOf("string");
- expect(event.participants[1].types).toStrictEqual(["Person"]);
- expect(event.participants[1].name).toBe("Bob");
- expect(event.participants[1].age).toBe(25);
- expect(event.participants[1].badges).toHaveLength(1);
- expect(event.participants[1].badges[0].name).toBe("Attendee");
-
- // Check author
- expect(event.author.id).toBeTypeOf("string");
- expect(event.author.types).toStrictEqual(["User", "Person"]);
- expect(event.author.name).toBe("Charlie");
- expect(event.author.email).toBe("charlie@example.com");
-
- expectTypeOf(event).toMatchTypeOf<{
- id: string;
- types: string[];
- name: string;
- participants: {
- id: string;
- types: string[];
- name: string;
- age: number;
- badges: {
- id: string;
- types: string[];
- name: string;
- }[];
- }[];
- author: {
- id: string;
- types: string[];
- name: string;
- age: number;
- email: string;
- badges: {
- id: string;
- types: string[];
- name: string;
- }[];
- };
- }>();
- });
-
- it("should create entities with nested relations and query them", () => {
- const { result: createResult } = renderHook(() => useCreateEntity(), {
- wrapper,
- });
-
- const { result: queryResult } = renderHook(
- () => useQuery({ types: ["Person"] }),
- { wrapper }
- );
-
- act(() => {
- createResult.current(["Person"], {
- name: "Dave",
- age: 40,
- badges: [{ name: "VIP" }, { name: "Contributor" }],
- });
- });
-
- // Wait for the hook to update
- const people = queryResult.current;
- expect(people).toHaveLength(1);
-
- const person = people[0];
- if (!person) {
- throw new Error("Person not found");
- }
- expect(person.name).toBe("Dave");
- expect(person.age).toBe(40);
- expect(person.badges).toHaveLength(2);
- if (!person.badges[0] || !person.badges[1]) {
- throw new Error("Badges not found");
- }
- expect(person.badges[0].name).toBe("VIP");
- expect(person.badges[1].name).toBe("Contributor");
- });
-
- it("should delete an entity", () => {
- const { result: createResult } = renderHook(() => useCreateEntity(), {
- wrapper,
- });
-
- const { result: deleteResult } = renderHook(() => useDeleteEntity(), {
- wrapper,
- });
-
- const { result: queryResult } = renderHook(
- () => useQuery({ types: ["Badge"] }),
- { wrapper }
- );
-
- let badgeId: string | undefined;
-
- act(() => {
- createResult.current(["Badge"], { name: "Exclusive" });
- });
-
- act(() => {
- const badges = queryResult.current;
- expect(badges).toHaveLength(1);
- badgeId = badges[0]?.id;
- });
-
- act(() => {
- const success = deleteResult.current(badgeId!);
- expect(success).toBe(true);
- });
-
- act(() => {
- const badgesAfterDelete = queryResult.current;
- expect(badgesAfterDelete).toHaveLength(0);
- });
- });
+it.skip("TODO test full integration", () => {
+ expect(true).toBe(false);
});
diff --git a/packages/graph-framework/src/index.tsx b/packages/graph-framework/src/index.tsx
index 45f75471..14dd81c5 100644
--- a/packages/graph-framework/src/index.tsx
+++ b/packages/graph-framework/src/index.tsx
@@ -1 +1,4 @@
-export * from "./context.js";
+export * from "graph-framework-identity";
+export * from "graph-framework-schema";
+export * from "graph-framework-space-events";
+export * from "graph-framework-utils";
diff --git a/packages/graph-framework/vite.config.js b/packages/graph-framework/vite.config.js
index e5bf5565..b4f8c666 100644
--- a/packages/graph-framework/vite.config.js
+++ b/packages/graph-framework/vite.config.js
@@ -5,7 +5,6 @@ import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
- // 👋 add the line below to add jsdom to vite
environment: "jsdom",
},
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 44cbef3e..2d0227f6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -238,15 +238,21 @@ importers:
packages/graph-framework:
dependencies:
- fast-deep-equal:
- specifier: ^3.1.3
- version: 3.1.3
+ graph-framework-identity:
+ specifier: workspace:*
+ version: link:../graph-framework-identity
+ graph-framework-schema:
+ specifier: workspace:*
+ version: link:../graph-framework-schema
+ graph-framework-space-events:
+ specifier: workspace:*
+ version: link:../graph-framework-space-events
+ graph-framework-utils:
+ specifier: workspace:*
+ version: link:../graph-framework-utils
react:
specifier: ^18
version: 18.3.1
- uuid:
- specifier: ^10.0.0
- version: 10.0.0
devDependencies:
'@automerge/automerge':
specifier: ^2.2.8
@@ -284,9 +290,112 @@ importers:
vitest:
specifier: ^2.1.1
version: 2.1.1(@types/node@22.7.4)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))
- zod:
- specifier: ^3.23.8
- version: 3.23.8
+
+ packages/graph-framework-identity:
+ dependencies:
+ graph-framework-utils:
+ specifier: workspace:*
+ version: link:../graph-framework-utils
+ uuid:
+ specifier: ^10.0.0
+ version: 10.0.0
+ devDependencies:
+ '@effect/schema':
+ specifier: ^0.74.1
+ version: 0.74.2(effect@3.8.4)
+ vite:
+ specifier: ^5.4.8
+ version: 5.4.8(@types/node@22.7.4)
+ vitest:
+ specifier: ^2.1.1
+ version: 2.1.2(@types/node@22.7.4)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+
+ packages/graph-framework-schema:
+ dependencies:
+ fast-deep-equal:
+ specifier: ^3.1.3
+ version: 3.1.3
+ graph-framework-utils:
+ specifier: workspace:*
+ version: link:../graph-framework-utils
+ react:
+ specifier: ^18
+ version: 18.3.1
+ devDependencies:
+ '@automerge/automerge':
+ specifier: ^2.2.8
+ version: 2.2.8
+ '@automerge/automerge-repo':
+ specifier: ^1.2.1
+ version: 1.2.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.4)(typescript@5.6.2)
+ '@automerge/automerge-repo-react-hooks':
+ specifier: ^1.2.1
+ version: 1.2.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)
+ '@effect/schema':
+ specifier: ^0.74.1
+ version: 0.74.2(effect@3.8.4)
+ '@testing-library/jest-dom':
+ specifier: ^6.5.0
+ version: 6.5.0
+ '@testing-library/react':
+ specifier: ^16.0.1
+ version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@types/react':
+ specifier: ^18.3.7
+ version: 18.3.11
+ '@types/uuid':
+ specifier: ^10.0.0
+ version: 10.0.0
+ '@vitejs/plugin-react':
+ specifier: ^4.3.2
+ version: 4.3.2(vite@5.4.8(@types/node@22.7.4))
+ jsdom:
+ specifier: ^25.0.1
+ version: 25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ vite:
+ specifier: ^5.4.8
+ version: 5.4.8(@types/node@22.7.4)
+ vitest:
+ specifier: ^2.1.1
+ version: 2.1.2(@types/node@22.7.4)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+
+ packages/graph-framework-space-events:
+ dependencies:
+ graph-framework-utils:
+ specifier: workspace:*
+ version: link:../graph-framework-utils
+ uuid:
+ specifier: ^10.0.0
+ version: 10.0.0
+ devDependencies:
+ '@effect/schema':
+ specifier: ^0.74.1
+ version: 0.74.2(effect@3.8.4)
+ vite:
+ specifier: ^5.4.8
+ version: 5.4.8(@types/node@22.7.4)
+ vitest:
+ specifier: ^2.1.1
+ version: 2.1.2(@types/node@22.7.4)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+
+ packages/graph-framework-utils:
+ dependencies:
+ uuid:
+ specifier: ^10.0.0
+ version: 10.0.0
+ devDependencies:
+ '@types/react':
+ specifier: ^18.3.7
+ version: 18.3.11
+ '@types/uuid':
+ specifier: ^10.0.0
+ version: 10.0.0
+ vite:
+ specifier: ^5.4.8
+ version: 5.4.8(@types/node@22.7.4)
+ vitest:
+ specifier: ^2.1.1
+ version: 2.1.2(@types/node@22.7.4)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))
packages:
@@ -7016,6 +7125,16 @@ snapshots:
lodash: 4.17.21
redent: 3.0.0
+ '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.7
+ '@testing-library/dom': 10.4.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.11
+ '@types/react-dom': 18.3.0
+
'@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.25.7