From 1a664e4c01fbc58470a7bc8f3ae06766c01d84a8 Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 1 Jul 2025 16:56:32 +0200 Subject: [PATCH 1/4] ensure navigations buttons are always visible --- apps/typesync/client/src/routes/apps/create.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/typesync/client/src/routes/apps/create.tsx b/apps/typesync/client/src/routes/apps/create.tsx index 4fc75f3e..06f0fe69 100644 --- a/apps/typesync/client/src/routes/apps/create.tsx +++ b/apps/typesync/client/src/routes/apps/create.tsx @@ -808,7 +808,7 @@ function CreateAppPage() { - + Cancel From 045c54b74b710d46484d268e7f2b7c8189bee88b Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 1 Jul 2025 18:21:14 +0200 Subject: [PATCH 2/4] fix setting directory --- .../src/Components/FormComponents/Input.tsx | 5 +++- .../client/src/routes/apps/create.tsx | 24 ++++++------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/apps/typesync/client/src/Components/FormComponents/Input.tsx b/apps/typesync/client/src/Components/FormComponents/Input.tsx index ff4c6aa8..40cbb2d7 100644 --- a/apps/typesync/client/src/Components/FormComponents/Input.tsx +++ b/apps/typesync/client/src/Components/FormComponents/Input.tsx @@ -33,7 +33,10 @@ export function FormComponentTextField({ id, label, hint, ...rest }: Readonly field.handleChange(e.target.value)} + onChange={(e) => { + field.handleChange(e.target.value); + rest.onChange?.(e); + }} data-state={hasErrors ? 'invalid' : undefined} aria-invalid={hasErrors ? 'true' : undefined} aria-describedby={hasErrors ? `${id}-invalid` : hint != null ? `${id}-hint` : undefined} diff --git a/apps/typesync/client/src/routes/apps/create.tsx b/apps/typesync/client/src/routes/apps/create.tsx index 06f0fe69..aca311db 100644 --- a/apps/typesync/client/src/routes/apps/create.tsx +++ b/apps/typesync/client/src/routes/apps/create.tsx @@ -366,23 +366,7 @@ function CreateAppPage() {

Create New App

- + {(field) => ( { + createAppForm.setFieldValue( + 'directory', + `./${pipe(e.target.value, EffectString.toLowerCase, EffectString.replaceAll(/\s/g, '-'))}`, + ); + }} /> )} From 4357e8da473185379b5dccbadea12f88231377e8 Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 1 Jul 2025 19:10:47 +0200 Subject: [PATCH 3/4] generate mapping file --- apps/typesync/src/Generator.ts | 51 +++++++++++++++++- apps/typesync/test/Generator.test.ts | 79 ++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 apps/typesync/test/Generator.test.ts diff --git a/apps/typesync/src/Generator.ts b/apps/typesync/src/Generator.ts index 17b9e557..0f9c1678 100644 --- a/apps/typesync/src/Generator.ts +++ b/apps/typesync/src/Generator.ts @@ -1,8 +1,8 @@ -import { execSync } from 'node:child_process'; -import { readdirSync } from 'node:fs'; import { FileSystem, Path, type Error as PlatformError } from '@effect/platform'; import { NodeFileSystem } from '@effect/platform-node'; import { Cause, Console, Data, Effect, String as EffectString } from 'effect'; +import { execSync } from 'node:child_process'; +import { readdirSync } from 'node:fs'; import * as Domain from '../domain/Domain.js'; import * as Utils from './Utils.js'; @@ -117,6 +117,7 @@ export class SchemaGenerator extends Effect.Service()('/typesyn ), Effect.andThen(() => updatePackageJson(app, directory)), Effect.andThen(() => fs.writeFileString(path.join(directory, 'src', 'schema.ts'), buildSchemaFile(app))), + Effect.andThen(() => fs.writeFileString(path.join(directory, 'src', 'mapping.ts'), buildMappingFile(app))), Effect.andThen(() => cleanup), ); @@ -290,9 +291,55 @@ ${fieldStrings.join(',\n')} function buildSchemaFile(schema: Domain.InsertAppSchema) { const importStatement = `import { Entity, Type } from '@graphprotocol/hypergraph';`; + const typeDefinitions = schema.types .map(typeDefinitionToString) .filter((def) => def != null) .join('\n\n'); return [importStatement, typeDefinitions].join('\n\n'); } + +export function buildMappingFile(schema: Domain.InsertAppSchema) { + const importStatement1 = `import { Id } from '@graphprotocol/grc-20';`; + const importStatement2 = `import type { Mapping } from '@graphprotocol/hypergraph';`; + + const typeMappings: string[] = []; + + for (const type of schema.types) { + const properties: string[] = []; + const relations: string[] = []; + + // Process properties and relations + for (const property of type.properties) { + if (Domain.isDataTypeRelation(property.dataType)) { + // This is a relation + relations.push(` ${Utils.toCamelCase(property.name)}: Id.Id('${property.knowledgeGraphId}')`); + } else { + // This is a regular property + properties.push(` ${Utils.toCamelCase(property.name)}: Id.Id('${property.knowledgeGraphId}')`); + } + } + + const typeMapping = ` ${type.name}: { + typeIds: [Id.Id('${type.knowledgeGraphId}')], + properties: { +${properties.join(',\n')}, + },${ + relations.length > 0 + ? ` + relations: { +${relations.join(',\n')}, + },` + : '' + } + }`; + + typeMappings.push(typeMapping); + } + + const mappingString = `export const mapping: Mapping = { +${typeMappings.join(',\n')}, +};`; + + return [importStatement1, importStatement2, '', mappingString].join('\n'); +} diff --git a/apps/typesync/test/Generator.test.ts b/apps/typesync/test/Generator.test.ts new file mode 100644 index 00000000..d2bd1295 --- /dev/null +++ b/apps/typesync/test/Generator.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from '@effect/vitest'; + +// @ts-ignore - fix the ts setup +import { buildMappingFile } from '../src/Generator.js'; + +describe('buildMappingFile', () => { + it('should build a valid mapping file', () => { + const expectedMapping = `import { Id } from '@graphprotocol/grc-20'; +import type { Mapping } from '@graphprotocol/hypergraph'; + +export const mapping: Mapping = { + Space: { + typeIds: [Id.Id('362c1dbd-dc64-44bb-a3c4-652f38a642d7')], + properties: { + name: Id.Id('a126ca53-0c8e-48d5-b888-82c734c38935'), + description: Id.Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'), + }, + }, + Activity: { + typeIds: [Id.Id('8275c359-4662-40fb-9aec-27177b520cd2')], + properties: { + name: Id.Id('a126ca53-0c8e-48d5-b888-82c734c38935'), + description: Id.Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'), + }, + relations: { + relatedSpaces: Id.Id('5b722cd3-61d6-494e-8887-1310566437ba'), + }, + }, +};`; + + const mapping = buildMappingFile({ + name: 'test', + description: 'test', + directory: 'test', + template: 'vite_react', + types: [ + { + name: 'Space', + knowledgeGraphId: '362c1dbd-dc64-44bb-a3c4-652f38a642d7', + properties: [ + { + name: 'Name', + knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935', + dataType: 'Text', + }, + { + name: 'Description', + knowledgeGraphId: '9b1f76ff-9711-404c-861e-59dc3fa7d037', + dataType: 'Text', + }, + ], + }, + { + name: 'Activity', + knowledgeGraphId: '8275c359-4662-40fb-9aec-27177b520cd2', + properties: [ + { + name: 'Name', + knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935', + dataType: 'Text', + }, + { + name: 'Description', + knowledgeGraphId: '9b1f76ff-9711-404c-861e-59dc3fa7d037', + dataType: 'Text', + }, + { + name: 'Related spaces', + knowledgeGraphId: '5b722cd3-61d6-494e-8887-1310566437ba', + dataType: 'Relation(Related spaces)', + relationType: 'Related spaces', + }, + ], + }, + ], + }); + expect(mapping).toBe(expectedMapping); + }); +}); From 265c0defe460d549bcbeec170d1b122c665e2d00 Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 1 Jul 2025 19:15:55 +0200 Subject: [PATCH 4/4] fix import order --- apps/typesync/src/Generator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/typesync/src/Generator.ts b/apps/typesync/src/Generator.ts index 0f9c1678..eaee75f9 100644 --- a/apps/typesync/src/Generator.ts +++ b/apps/typesync/src/Generator.ts @@ -1,8 +1,8 @@ +import { execSync } from 'node:child_process'; +import { readdirSync } from 'node:fs'; import { FileSystem, Path, type Error as PlatformError } from '@effect/platform'; import { NodeFileSystem } from '@effect/platform-node'; import { Cause, Console, Data, Effect, String as EffectString } from 'effect'; -import { execSync } from 'node:child_process'; -import { readdirSync } from 'node:fs'; import * as Domain from '../domain/Domain.js'; import * as Utils from './Utils.js';