Skip to content

Commit e533b53

Browse files
committed
convert to dashless UUIDs (Geo ID)
1 parent 858c381 commit e533b53

22 files changed

Lines changed: 359 additions & 282 deletions

packages/hypergraph/src/cli/services/Model.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Schema } from 'effect';
22

33
import * as Mapping from '../../mapping/Mapping.js';
44
import * as Utils from '../../mapping/Utils.js';
5+
import { GeoIdSchema } from '../../utils/geo-id.js';
56

67
export const TypesyncHypergraphSchemaStatus = Schema.NullOr(
78
Schema.Literal(
@@ -65,20 +66,20 @@ export class TypesyncHypergraphSchema extends Schema.Class<TypesyncHypergraphSch
6566
export const TypesyncHypergraphMapping = Schema.Record({
6667
key: Schema.NonEmptyTrimmedString,
6768
value: Schema.Struct({
68-
typeIds: Schema.Array(Schema.UUID).pipe(Schema.minItems(1)),
69+
typeIds: Schema.Array(GeoIdSchema).pipe(Schema.minItems(1)),
6970
properties: Schema.optional(
7071
Schema.UndefinedOr(
7172
Schema.Record({
7273
key: Schema.NonEmptyTrimmedString,
73-
value: Schema.UUID,
74+
value: GeoIdSchema,
7475
}),
7576
),
7677
),
7778
relations: Schema.optional(
7879
Schema.UndefinedOr(
7980
Schema.Record({
8081
key: Schema.NonEmptyTrimmedString,
81-
value: Schema.UUID,
82+
value: GeoIdSchema,
8283
}),
8384
),
8485
),

packages/hypergraph/src/cli/services/Utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,13 @@ export function buildMappingFile(mapping: Mapping.Mapping | Model.TypesyncHyperg
369369
* types: [
370370
* {
371371
* name: "User",
372-
* knowledgeGraphId: "7f9562d4-034d-4385-bf5c-f02cdebba47a",
372+
* knowledgeGraphId: "7f9562d4034d4385bf5cf02cdebba47a",
373373
* status: null,
374374
* properties: [
375375
* {
376376
* name: "name",
377377
* dataType: "String",
378-
* knowledgeGraphId: "a126ca53-0c8e-48d5-b888-82c734c38935",
378+
* knowledgeGraphId: "a126ca530c8e48d5b88882c734c38935",
379379
* optional: null,
380380
* status: null
381381
* }
@@ -391,9 +391,9 @@ export function buildMappingFile(mapping: Mapping.Mapping | Model.TypesyncHyperg
391391
*
392392
* export const mapping: Mapping = {
393393
* User: {
394-
* typeIds: [Id('7f9562d4-034d-4385-bf5c-f02cdebba47a')],
394+
* typeIds: [Id('7f9562d4034d4385bf5cf02cdebba47a')],
395395
* properties: {
396-
* name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
396+
* name: Id('a126ca530c8e48d5b88882c734c38935'),
397397
* }
398398
* }
399399
* }

packages/hypergraph/src/entity/find-many-public.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const buildEntitiesQuery = (
7777
query ${queryName}(${variableDefinitions}) {
7878
entities: ${queryName}(
7979
${orderByArgs}filter: { and: [{
80-
relations: {some: {typeId: {is: "8f151ba4-de20-4e3c-9cb4-99ddf96f48f1"}, toEntityId: {in: $typeIds}}},
80+
relations: {some: {typeId: {is: "8f151ba4de204e3c9cb499ddf96f48f1"}, toEntityId: {in: $typeIds}}},
8181
${entitySpaceFilter}
8282
}, $filter]}
8383
first: $first

packages/hypergraph/src/mapping/Mapping.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type CreatePropertyParams, Graph, Id as Grc20Id, type Op } from '@graphprotocol/grc-20';
22
import { Data, Array as EffectArray, Schema as EffectSchema, Option, pipe } from 'effect';
3-
3+
import { GeoIdSchema } from '../utils/geo-id.js';
44
import { namesAreUnique, toCamelCase, toPascalCase } from './Utils.js';
55

66
/**
@@ -46,20 +46,20 @@ export type MappingEntry = {
4646
*
4747
* const mapping: Mapping = {
4848
* Account: {
49-
* typeIds: [Id('a5fd07b1-120f-46c6-b46f-387ef98396a6')],
49+
* typeIds: [Id('a5fd07b1120f46c6b46f387ef98396a6')],
5050
* properties: {
51-
* username: Id('994edcff-6996-4a77-9797-a13e5e3efad8'),
52-
* createdAt: Id('64bfba51-a69b-4746-be4b-213214a879fe')
51+
* username: Id('994edcff69964a779797a13e5e3efad8'),
52+
* createdAt: Id('64bfba51a69b4746be4b213214a879fe')
5353
* }
5454
* },
5555
* Event: {
56-
* typeIds: [Id('0349187b-526f-435f-b2bb-9e9caf23127a')],
56+
* typeIds: [Id('0349187b526f435fb2bb9e9caf23127a')],
5757
* properties: {
58-
* name: Id('3808e060-fb4a-4d08-8069-35b8c8a1902b'),
59-
* description: Id('1f0d9007-8da2-4b28-ab9f-3bc0709f4837'),
58+
* name: Id('3808e060fb4a4d08806935b8c8a1902b'),
59+
* description: Id('1f0d90078da24b28ab9f3bc0709f4837'),
6060
* },
6161
* relations: {
62-
* speaker: Id('a5fd07b1-120f-46c6-b46f-387ef98396a6')
62+
* speaker: Id('a5fd07b1120f46c6b46f387ef98396a6')
6363
* }
6464
* }
6565
* }
@@ -132,7 +132,7 @@ export function getDataType(val: string): SchemaDataType {
132132

133133
const BaseSchemaTypeProperty = EffectSchema.Struct({
134134
name: EffectSchema.NonEmptyTrimmedString,
135-
knowledgeGraphId: EffectSchema.NullOr(EffectSchema.UUID),
135+
knowledgeGraphId: EffectSchema.NullOr(GeoIdSchema),
136136
/**
137137
* @since 0.4.0
138138
*/
@@ -181,7 +181,7 @@ export function propertyIsRelation(
181181
*/
182182
export const SchemaType = EffectSchema.Struct({
183183
name: EffectSchema.NonEmptyTrimmedString,
184-
knowledgeGraphId: EffectSchema.NullOr(EffectSchema.UUID),
184+
knowledgeGraphId: EffectSchema.NullOr(GeoIdSchema),
185185
properties: EffectSchema.Array(EffectSchema.Union(SchemaTypePropertyPrimitive, SchemaTypePropertyRelation)).pipe(
186186
EffectSchema.minItems(1),
187187
EffectSchema.filter(namesAreUnique, {
@@ -233,12 +233,12 @@ export const Schema = EffectSchema.Struct({
233233
types: [
234234
{
235235
name: 'Account',
236-
knowledgeGraphId: 'a5fd07b1-120f-46c6-b46f-387ef98396a6',
236+
knowledgeGraphId: 'a5fd07b1120f46c6b46f387ef98396a6',
237237
properties: [
238238
{
239239
name: 'name',
240240
optional: true,
241-
knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935',
241+
knowledgeGraphId: 'a126ca530c8e48d5b88882c734c38935',
242242
dataType: 'String',
243243
},
244244
],
@@ -530,12 +530,12 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
530530
* types: [
531531
* {
532532
* name: "Account",
533-
* knowledgeGraphId: "a5fd07b1-120f-46c6-b46f-387ef98396a6",
533+
* knowledgeGraphId: "a5fd07b1120f46c6b46f387ef98396a6",
534534
* properties: [
535535
* {
536536
* name: "username",
537537
* dataType: "String",
538-
* knowledgeGraphId: "994edcff-6996-4a77-9797-a13e5e3efad8"
538+
* knowledgeGraphId: "994edcff69964a779797a13e5e3efad8"
539539
* },
540540
* {
541541
* name: "createdAt",
@@ -551,7 +551,7 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
551551
* {
552552
* name: "name",
553553
* dataType: "String",
554-
* knowledgeGraphId: "3808e060-fb4a-4d08-8069-35b8c8a1902b"
554+
* knowledgeGraphId: "3808e060fb4a4d08806935b8c8a1902b"
555555
* },
556556
* {
557557
* name: "description",
@@ -572,20 +572,20 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
572572
*
573573
* expect(mapping).toEqual({
574574
* Account: {
575-
* typeIds: [Id("a5fd07b1-120f-46c6-b46f-387ef98396a6")], // comes from input schema
575+
* typeIds: [Id("a5fd07b1120f46c6b46f387ef98396a6")], // comes from input schema
576576
* properties: {
577-
* username: Id("994edcff-6996-4a77-9797-a13e5e3efad8"), // comes from input schema
578-
* createdAt: Id("8cd7d9ac-a878-4287-8000-e71e6f853117"), // generated from Graph.createProperty Op
577+
* username: Id("994edcff69964a779797a13e5e3efad8"), // comes from input schema
578+
* createdAt: Id("8cd7d9aca87842878000e71e6f853117"), // generated from Graph.createProperty Op
579579
* }
580580
* },
581581
* Event: {
582-
* typeIds: [Id("20b3fe39-8e62-41a0-b9cb-92743fd760da")], // generated from Graph.createType Op
582+
* typeIds: [Id("20b3fe398e6241a0b9cb92743fd760da")], // generated from Graph.createType Op
583583
* properties: {
584-
* name: Id("3808e060-fb4a-4d08-8069-35b8c8a1902b"), // comes from input schema
585-
* description: Id("8fc4e17c-7581-4d6c-a712-943385afc7b5"), // generated from Graph.createProperty Op
584+
* name: Id("3808e060fb4a4d08806935b8c8a1902b"), // comes from input schema
585+
* description: Id("8fc4e17c75814d6ca712943385afc7b5"), // generated from Graph.createProperty Op
586586
* },
587587
* relations: {
588-
* speaker: Id("651ce59f-643b-4931-bf7a-5dc0ca0f5a47"), // generated from Graph.createProperty Op
588+
* speaker: Id("651ce59f643b4931bf7a5dc0ca0f5a47"), // generated from Graph.createProperty Op
589589
* }
590590
* }
591591
* })
@@ -594,31 +594,31 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
594594
* {
595595
* type: "CREATE_PROPERTY",
596596
* property: {
597-
* id: Id("8cd7d9ac-a878-4287-8000-e71e6f853117"),
597+
* id: Id("8cd7d9aca87842878000e71e6f853117"),
598598
* dataType: "String"
599599
* }
600600
* },
601601
* // Graph.createProperty Op for Event.description property
602602
* {
603603
* type: "CREATE_PROPERTY",
604604
* property: {
605-
* id: Id("8fc4e17c-7581-4d6c-a712-943385afc7b5"),
605+
* id: Id("8fc4e17c75814d6ca712943385afc7b5"),
606606
* dataType: "String"
607607
* }
608608
* },
609609
* // Graph.createProperty Op for Event.speaker property
610610
* {
611611
* type: "CREATE_PROPERTY",
612612
* property: {
613-
* id: Id("651ce59f-643b-4931-bf7a-5dc0ca0f5a47"),
613+
* id: Id("651ce59f643b4931bf7a5dc0ca0f5a47"),
614614
* dataType: "RELATION"
615615
* }
616616
* },
617617
* // Graph.createType Op for Event type
618618
* {
619619
* type: "CREATE_PROPERTY",
620620
* property: {
621-
* id: Id("651ce59f-643b-4931-bf7a-5dc0ca0f5a47"),
621+
* id: Id("651ce59f643b4931bf7a5dc0ca0f5a47"),
622622
* dataType: "RELATION"
623623
* }
624624
* },
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import bs58check from 'bs58check';
22
import { parse as parseUuid, stringify as stringifyUuid } from 'uuid';
3+
import { normalizeGeoId, toUuid } from './geo-id.js';
34

45
/**
5-
* Converts a UUID into Base58Check
6+
* Converts a Geo ID (UUID without dashes) into Base58Check
67
*/
7-
export function idToAutomergeId(uuid: string) {
8-
const payload = parseUuid(uuid);
8+
export function idToAutomergeId(id: string) {
9+
// `id` parsing expects canonical formatting; accept dashless IDs too.
10+
const payload = parseUuid(toUuid(id));
911
return bs58check.encode(payload);
1012
}
1113

@@ -14,5 +16,6 @@ export function idToAutomergeId(uuid: string) {
1416
*/
1517
export function automergeIdToId(base58CheckUuid: string) {
1618
const versionedPayload = bs58check.decode(base58CheckUuid);
17-
return stringifyUuid(versionedPayload);
19+
// Return dashless IDs to align with GRC-20 / API conventions.
20+
return normalizeGeoId(stringifyUuid(versionedPayload));
1821
}

packages/hypergraph/src/utils/generateId.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { v4 as uuidv4 } from 'uuid';
2+
import { normalizeGeoId } from './geo-id.js';
23

34
/**
45
* Generate a v4 UUID.
@@ -14,5 +15,6 @@ import { v4 as uuidv4 } from 'uuid';
1415
* @returns v4 UUID
1516
*/
1617
export function generateId() {
17-
return uuidv4();
18+
// GRC-20 now uses UUIDs without dashes; normalize for internal consistency.
19+
return normalizeGeoId(uuidv4());
1820
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Schema } from 'effect';
2+
3+
/**
4+
* Hypergraph Geo IDs are UUIDs without dashes (32 hex chars).
5+
*
6+
* Since older code and tooling may still provide UUIDs with dashes, we accept both
7+
* and normalize to the dashless format.
8+
*/
9+
export type GeoId = string;
10+
11+
const UUID_WITH_DASHES_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12+
const UUID_WITHOUT_DASHES_REGEX = /^[0-9a-f]{32}$/i;
13+
14+
export function normalizeGeoId(id: string): GeoId {
15+
return id.replaceAll('-', '').toLowerCase();
16+
}
17+
18+
export function isGeoId(id: string): boolean {
19+
if (UUID_WITHOUT_DASHES_REGEX.test(id)) return true;
20+
if (UUID_WITH_DASHES_REGEX.test(id)) return true;
21+
return UUID_WITHOUT_DASHES_REGEX.test(normalizeGeoId(id));
22+
}
23+
24+
export function parseGeoId(id: string): GeoId {
25+
const normalized = normalizeGeoId(id);
26+
if (!UUID_WITHOUT_DASHES_REGEX.test(normalized)) {
27+
throw new Error(`Invalid Geo ID (expected UUID with or without dashes): ${id}`);
28+
}
29+
return normalized;
30+
}
31+
32+
export function toUuid(id: string): string {
33+
const normalized = parseGeoId(id);
34+
return [
35+
normalized.slice(0, 8),
36+
normalized.slice(8, 12),
37+
normalized.slice(12, 16),
38+
normalized.slice(16, 20),
39+
normalized.slice(20),
40+
].join('-');
41+
}
42+
43+
/**
44+
* An Effect Schema that accepts UUIDs with or without dashes and always decodes/encodes
45+
* to the dashless (normalized) `GeoId` format.
46+
*/
47+
export const GeoIdSchema = Schema.transform(Schema.String, Schema.String, {
48+
strict: true,
49+
decode: (value: string) => parseGeoId(value),
50+
encode: (value: string) => parseGeoId(value),
51+
}).pipe(Schema.annotations({ identifier: 'GeoId' }));

packages/hypergraph/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './base58.js';
55
export * from './convert-property-value.js';
66
export * from './convert-relations.js';
77
export * from './generateId.js';
8+
export * from './geo-id.js';
89
export * from './get-relation-type-ids.js';
910
export * from './hexBytesAddressUtils.js';
1011
export * from './isRelation.js';

0 commit comments

Comments
 (0)