@graphprotocol/hypergraph 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/Cli.d.ts +2 -0
- package/dist/cli/Cli.d.ts.map +1 -0
- package/dist/cli/Cli.js +8 -0
- package/dist/cli/Cli.js.map +1 -0
- package/dist/cli/Logger.d.ts +3 -0
- package/dist/cli/Logger.d.ts.map +1 -0
- package/dist/cli/Logger.js +20 -0
- package/dist/cli/Logger.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/bun.d.ts +3 -0
- package/dist/cli/bun.d.ts.map +1 -0
- package/dist/cli/bun.js +3 -0
- package/dist/cli/bun.js.map +1 -0
- package/dist/cli/services/Typesync.d.ts +21 -0
- package/dist/cli/services/Typesync.d.ts.map +1 -0
- package/dist/cli/services/Typesync.js +137 -0
- package/dist/cli/services/Typesync.js.map +1 -0
- package/dist/cli/services/Utils.d.ts +10 -0
- package/dist/cli/services/Utils.d.ts.map +1 -0
- package/dist/cli/services/Utils.js +154 -0
- package/dist/cli/services/Utils.js.map +1 -0
- package/dist/cli/subcommands/typesync.d.ts +7 -0
- package/dist/cli/subcommands/typesync.d.ts.map +1 -0
- package/dist/cli/subcommands/typesync.js +38 -0
- package/dist/cli/subcommands/typesync.js.map +1 -0
- package/dist/entity/findMany.js.map +1 -1
- package/dist/entity/types.d.ts +4 -4
- package/dist/entity/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mapping/Mapping.d.ts +51 -39
- package/dist/mapping/Mapping.d.ts.map +1 -1
- package/dist/mapping/Mapping.js +49 -27
- package/dist/mapping/Mapping.js.map +1 -1
- package/dist/type/type.d.ts +3 -2
- package/dist/type/type.d.ts.map +1 -1
- package/dist/type/type.js +5 -2
- package/dist/type/type.js.map +1 -1
- package/dist/type-utils/type-utils.d.ts +7 -0
- package/dist/type-utils/type-utils.d.ts.map +1 -0
- package/dist/type-utils/type-utils.js +41 -0
- package/dist/type-utils/type-utils.js.map +1 -0
- package/package.json +7 -3
- package/src/cli/Cli.ts +15 -0
- package/src/cli/Logger.ts +20 -0
- package/src/cli/bin.ts +33 -0
- package/src/cli/bun.ts +3 -0
- package/src/cli/services/Typesync.ts +189 -0
- package/src/cli/services/Utils.ts +187 -0
- package/src/cli/subcommands/typesync.ts +93 -0
- package/src/entity/findMany.ts +4 -4
- package/src/entity/types.ts +4 -4
- package/src/index.ts +2 -0
- package/src/mapping/Mapping.ts +65 -43
- package/src/type/type.ts +6 -2
- package/src/type-utils/type-utils.ts +46 -0
- package/dist/Entity.d.ts +0 -69
- package/dist/Entity.d.ts.map +0 -1
- package/dist/Entity.js +0 -174
- package/dist/Entity.js.map +0 -1
- package/dist/identity/create-identity-keys.d.ts +0 -3
- package/dist/identity/create-identity-keys.d.ts.map +0 -1
- package/dist/identity/create-identity-keys.js +0 -20
- package/dist/identity/create-identity-keys.js.map +0 -1
- package/dist/identity/login.d.ts +0 -38
- package/dist/identity/login.d.ts.map +0 -1
- package/dist/identity/login.js +0 -241
- package/dist/identity/login.js.map +0 -1
- package/dist/utils/hasArrayField.d.ts +0 -2
- package/dist/utils/hasArrayField.d.ts.map +0 -1
- package/dist/utils/hasArrayField.js +0 -5
- package/dist/utils/hasArrayField.js.map +0 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { Data, Effect, Array as EffectArray } from 'effect';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
|
|
4
|
+
import * as Mapping from '../../mapping/Mapping.js';
|
|
5
|
+
import * as Utils from '../../mapping/Utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Takes a parsed schema.ts file and maps it to a the Mapping.Schema type.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*
|
|
12
|
+
* @param sourceCode the read schema.ts file
|
|
13
|
+
* @param mapping the parsed mappint.ts file
|
|
14
|
+
* @returns the parsed Schema instance
|
|
15
|
+
*/
|
|
16
|
+
export const parseSchema = (
|
|
17
|
+
sourceCode: string,
|
|
18
|
+
mapping: Mapping.Mapping,
|
|
19
|
+
): Effect.Effect<Mapping.Schema, SchemaParserFailure> =>
|
|
20
|
+
Effect.try({
|
|
21
|
+
try() {
|
|
22
|
+
const sourceFile = ts.createSourceFile('schema.ts', sourceCode, ts.ScriptTarget.Latest, true);
|
|
23
|
+
|
|
24
|
+
const entities: Array<Mapping.SchemaType> = [];
|
|
25
|
+
|
|
26
|
+
const visit = (node: ts.Node) => {
|
|
27
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
28
|
+
const className = node.name.text;
|
|
29
|
+
const properties: Array<Mapping.SchemaTypePropertyPrimitive | Mapping.SchemaTypePropertyRelation> = [];
|
|
30
|
+
|
|
31
|
+
// Find the Entity.Class call
|
|
32
|
+
if (node.heritageClauses) {
|
|
33
|
+
for (const clause of node.heritageClauses) {
|
|
34
|
+
for (const type of clause.types) {
|
|
35
|
+
if (ts.isCallExpression(type.expression)) {
|
|
36
|
+
const callExpr = type.expression;
|
|
37
|
+
|
|
38
|
+
// Look for the object literal with properties
|
|
39
|
+
if (callExpr.arguments.length > 0) {
|
|
40
|
+
const arg = callExpr.arguments[0];
|
|
41
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
42
|
+
for (const prop of arg.properties) {
|
|
43
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
44
|
+
const propName = prop.name.text;
|
|
45
|
+
let dataType: Mapping.SchemaDataType = 'String';
|
|
46
|
+
let relationType: string | undefined;
|
|
47
|
+
|
|
48
|
+
const mappingEntry = mapping[className];
|
|
49
|
+
const camelCasePropName = Utils.toCamelCase(propName);
|
|
50
|
+
|
|
51
|
+
// Extract the type
|
|
52
|
+
if (ts.isPropertyAccessExpression(prop.initializer)) {
|
|
53
|
+
// Simple types like Type.Text
|
|
54
|
+
dataType = Mapping.getDataType(prop.initializer.name.text);
|
|
55
|
+
|
|
56
|
+
// Look up the property's knowledgeGraphId from the mapping
|
|
57
|
+
const propKnowledgeGraphId = mappingEntry?.properties?.[camelCasePropName] || null;
|
|
58
|
+
|
|
59
|
+
// push to type properties as primitive property
|
|
60
|
+
properties.push({
|
|
61
|
+
name: propName,
|
|
62
|
+
dataType: dataType as Mapping.SchemaDataTypePrimitive,
|
|
63
|
+
knowledgeGraphId: propKnowledgeGraphId,
|
|
64
|
+
} satisfies Mapping.SchemaTypePropertyPrimitive);
|
|
65
|
+
} else if (ts.isCallExpression(prop.initializer)) {
|
|
66
|
+
// Relation types like Type.Relation(User)
|
|
67
|
+
const callNode = prop.initializer;
|
|
68
|
+
if (ts.isPropertyAccessExpression(callNode.expression)) {
|
|
69
|
+
const typeName = callNode.expression.name.text;
|
|
70
|
+
|
|
71
|
+
if (typeName === 'Relation' && callNode.arguments.length > 0) {
|
|
72
|
+
const relationArg = callNode.arguments[0];
|
|
73
|
+
if (ts.isIdentifier(relationArg)) {
|
|
74
|
+
relationType = relationArg.text;
|
|
75
|
+
dataType = `Relation(${relationType})`;
|
|
76
|
+
|
|
77
|
+
// Look up the relation property's knowledgeGraphId from the mapping
|
|
78
|
+
const relKnowledgeGraphId = mappingEntry?.relations?.[camelCasePropName] || null;
|
|
79
|
+
|
|
80
|
+
// push to type properties as relation property
|
|
81
|
+
properties.push({
|
|
82
|
+
name: propName,
|
|
83
|
+
dataType,
|
|
84
|
+
relationType,
|
|
85
|
+
knowledgeGraphId: relKnowledgeGraphId,
|
|
86
|
+
} satisfies Mapping.SchemaTypePropertyRelation);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Look up the type's knowledgeGraphId from the mapping
|
|
101
|
+
const mappingEntry = mapping[Utils.toPascalCase(className)];
|
|
102
|
+
const typeKnowledgeGraphId = mappingEntry?.typeIds?.[0] ? mappingEntry.typeIds[0] : null;
|
|
103
|
+
|
|
104
|
+
entities.push({ name: className, knowledgeGraphId: typeKnowledgeGraphId, properties });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
ts.forEachChild(node, visit);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
visit(sourceFile);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
types: entities,
|
|
114
|
+
} as const;
|
|
115
|
+
},
|
|
116
|
+
catch(error) {
|
|
117
|
+
return new SchemaParserFailure({
|
|
118
|
+
message: `Failed to parse schema: ${error}`,
|
|
119
|
+
cause: error,
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export class SchemaParserFailure extends Data.TaggedError('/Hypergraph/cli/errors/SchemaParserFailure')<{
|
|
125
|
+
readonly message: string;
|
|
126
|
+
readonly cause: unknown;
|
|
127
|
+
}> {}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @internal
|
|
131
|
+
*
|
|
132
|
+
* Runtime check to see if a value looks like a Mapping
|
|
133
|
+
*/
|
|
134
|
+
function isMappingLike(value: unknown): value is Mapping.Mapping {
|
|
135
|
+
if (!value || typeof value !== 'object') return false;
|
|
136
|
+
return Object.values(value).every(
|
|
137
|
+
(entry) =>
|
|
138
|
+
entry &&
|
|
139
|
+
typeof entry === 'object' &&
|
|
140
|
+
'typeIds' in entry &&
|
|
141
|
+
// biome-ignore lint/suspicious/noExplicitAny: parsing so type unknown
|
|
142
|
+
EffectArray.isArray((entry as any).typeIds),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @internal
|
|
148
|
+
*
|
|
149
|
+
* Look at the exported object from the mapping.ts file loaded via jiti and try to pull out the hypergraph mapping.
|
|
150
|
+
* Default should be:
|
|
151
|
+
* ```typescript
|
|
152
|
+
* export const mapping: Mapping
|
|
153
|
+
* ```
|
|
154
|
+
* But this is not guaranteed. This function looks through the file to try to find it using some logical defaults/checks.
|
|
155
|
+
*
|
|
156
|
+
* @param moduleExport the object imported from jiti from the mapping.ts file
|
|
157
|
+
*/
|
|
158
|
+
// biome-ignore lint/suspicious/noExplicitAny: type should be import object from jiti
|
|
159
|
+
export function parseHypergraphMapping(moduleExport: any): Mapping.Mapping {
|
|
160
|
+
// Handle null/undefined inputs
|
|
161
|
+
if (!moduleExport || typeof moduleExport !== 'object') {
|
|
162
|
+
return {} as Mapping.Mapping;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Find all exports that look like Mapping objects
|
|
166
|
+
const mappingCandidates = Object.entries(moduleExport).filter(([, value]) => isMappingLike(value));
|
|
167
|
+
|
|
168
|
+
if (mappingCandidates.length === 0) {
|
|
169
|
+
return {} as Mapping.Mapping;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (mappingCandidates.length === 1) {
|
|
173
|
+
return mappingCandidates[0][1] as Mapping.Mapping;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Multiple candidates - prefer common names
|
|
177
|
+
const preferredNames = ['mapping', 'default', 'config'];
|
|
178
|
+
for (const preferredName of preferredNames) {
|
|
179
|
+
const found = mappingCandidates.find(([name]) => name === preferredName);
|
|
180
|
+
if (found) {
|
|
181
|
+
return found[1] as Mapping.Mapping;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// If no preferred names found, use the first one
|
|
186
|
+
return mappingCandidates[0][1] as Mapping.Mapping;
|
|
187
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { Command, Options } from '@effect/cli';
|
|
3
|
+
import {
|
|
4
|
+
HttpApi,
|
|
5
|
+
HttpApiBuilder,
|
|
6
|
+
HttpApiEndpoint,
|
|
7
|
+
HttpApiError,
|
|
8
|
+
HttpApiGroup,
|
|
9
|
+
HttpApiSchema,
|
|
10
|
+
HttpMiddleware,
|
|
11
|
+
HttpServer,
|
|
12
|
+
HttpServerResponse,
|
|
13
|
+
} from '@effect/platform';
|
|
14
|
+
import { NodeHttpServer } from '@effect/platform-node';
|
|
15
|
+
import { AnsiDoc } from '@effect/printer-ansi';
|
|
16
|
+
import { Effect, Layer, Schema } from 'effect';
|
|
17
|
+
import * as Typesync from '../services/Typesync.js';
|
|
18
|
+
|
|
19
|
+
const hypergraphTypeSyncApi = HttpApi.make('HypergraphTypeSyncApi')
|
|
20
|
+
.add(
|
|
21
|
+
HttpApiGroup.make('SchemaStreamGroup')
|
|
22
|
+
.add(
|
|
23
|
+
// exposes an api endpoint at /api/vX/schema/events that is a stream of the current Schema parsed from the directory the hypergraph-cli tool is running in
|
|
24
|
+
HttpApiEndpoint.get('HypergraphSchemaEventStream')`/schema/events`
|
|
25
|
+
.addError(HttpApiError.InternalServerError)
|
|
26
|
+
.addSuccess(
|
|
27
|
+
Schema.String.pipe(
|
|
28
|
+
HttpApiSchema.withEncoding({
|
|
29
|
+
kind: 'Json',
|
|
30
|
+
contentType: 'text/event-stream',
|
|
31
|
+
}),
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
.prefix('/v1'),
|
|
36
|
+
)
|
|
37
|
+
.prefix('/api');
|
|
38
|
+
|
|
39
|
+
const hypergraphTypeSyncApiLive = HttpApiBuilder.group(hypergraphTypeSyncApi, 'SchemaStreamGroup', (handlers) =>
|
|
40
|
+
handlers.handle('HypergraphSchemaEventStream', () =>
|
|
41
|
+
Effect.gen(function* () {
|
|
42
|
+
const schemaStream = yield* Typesync.TypesyncSchemaStreamBuilder;
|
|
43
|
+
|
|
44
|
+
const stream = yield* schemaStream
|
|
45
|
+
.hypergraphSchemaStream()
|
|
46
|
+
.pipe(Effect.catchAll(() => new HttpApiError.InternalServerError()));
|
|
47
|
+
|
|
48
|
+
return yield* HttpServerResponse.stream(stream, { contentType: 'text/event-stream' }).pipe(
|
|
49
|
+
HttpServerResponse.setHeaders({
|
|
50
|
+
'Content-Type': 'text/event-stream',
|
|
51
|
+
'Cache-Control': 'no-cache',
|
|
52
|
+
Connection: 'keep-alive',
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const HypergraphTypeSyncApiLive = HttpApiBuilder.api(hypergraphTypeSyncApi).pipe(
|
|
60
|
+
Layer.provide(hypergraphTypeSyncApiLive),
|
|
61
|
+
Layer.provide(Typesync.layer),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const HypergraphTypeSyncApiLayer = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
|
|
65
|
+
Layer.provide(HttpApiBuilder.middlewareCors()),
|
|
66
|
+
Layer.provide(HypergraphTypeSyncApiLive),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
export const typesync = Command.make('typesync', {
|
|
70
|
+
args: {
|
|
71
|
+
port: Options.integer('port').pipe(
|
|
72
|
+
Options.withAlias('p'),
|
|
73
|
+
Options.withDefault(3000),
|
|
74
|
+
Options.withDescription('The port to run the Hypergraph TypeSync studio server on. Default 3000'),
|
|
75
|
+
),
|
|
76
|
+
},
|
|
77
|
+
}).pipe(
|
|
78
|
+
Command.withDescription(
|
|
79
|
+
'Opens the TypeSync studio to help users build and publish their Hypergraph application schema',
|
|
80
|
+
),
|
|
81
|
+
Command.withHandler(({ args }) =>
|
|
82
|
+
Effect.gen(function* () {
|
|
83
|
+
yield* HypergraphTypeSyncApiLayer.pipe(
|
|
84
|
+
HttpServer.withLogAddress,
|
|
85
|
+
Layer.provide(NodeHttpServer.layer(createServer, { port: args.port })),
|
|
86
|
+
Layer.tap(() =>
|
|
87
|
+
Effect.logInfo(AnsiDoc.text(`🎉 TypeSync studio started and running at http://localhost:${args.port}`)),
|
|
88
|
+
),
|
|
89
|
+
Layer.launch,
|
|
90
|
+
);
|
|
91
|
+
}),
|
|
92
|
+
),
|
|
93
|
+
);
|
package/src/entity/findMany.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
EntityFieldFilter,
|
|
16
16
|
EntityFilter,
|
|
17
17
|
EntityNumberFilter,
|
|
18
|
-
|
|
18
|
+
EntityStringFilter,
|
|
19
19
|
} from './types.js';
|
|
20
20
|
|
|
21
21
|
const documentChangeListener: {
|
|
@@ -298,19 +298,19 @@ export function findMany<const S extends AnyNoContext>(
|
|
|
298
298
|
|
|
299
299
|
if (typeof fieldValue === 'string') {
|
|
300
300
|
if ('startsWith' in fieldFilter) {
|
|
301
|
-
const textFilter = fieldFilter as
|
|
301
|
+
const textFilter = fieldFilter as EntityStringFilter;
|
|
302
302
|
if (textFilter.startsWith !== undefined && !fieldValue.startsWith(textFilter.startsWith)) {
|
|
303
303
|
return false;
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
if ('endsWith' in fieldFilter) {
|
|
307
|
-
const textFilter = fieldFilter as
|
|
307
|
+
const textFilter = fieldFilter as EntityStringFilter;
|
|
308
308
|
if (textFilter.endsWith !== undefined && !fieldValue.endsWith(textFilter.endsWith)) {
|
|
309
309
|
return false;
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
if ('contains' in fieldFilter) {
|
|
313
|
-
const textFilter = fieldFilter as
|
|
313
|
+
const textFilter = fieldFilter as EntityStringFilter;
|
|
314
314
|
if (textFilter.contains !== undefined && !fieldValue.includes(textFilter.contains)) {
|
|
315
315
|
return false;
|
|
316
316
|
}
|
package/src/entity/types.ts
CHANGED
|
@@ -47,7 +47,7 @@ export type DocumentContent = {
|
|
|
47
47
|
relations?: Record<string, DocumentRelation>;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
export type
|
|
50
|
+
export type EntityBooleanFilter = {
|
|
51
51
|
is: boolean;
|
|
52
52
|
};
|
|
53
53
|
|
|
@@ -59,14 +59,14 @@ export type EntityNumberFilter = {
|
|
|
59
59
|
or?: EntityNumberFilter[];
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
export type
|
|
62
|
+
export type EntityStringFilter = {
|
|
63
63
|
is?: string;
|
|
64
64
|
startsWith?: string;
|
|
65
65
|
endsWith?: string;
|
|
66
66
|
contains?: string;
|
|
67
67
|
equals?: string;
|
|
68
|
-
not?:
|
|
69
|
-
or?:
|
|
68
|
+
not?: EntityStringFilter;
|
|
69
|
+
or?: EntityStringFilter[];
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
export type CrossFieldFilter<T> = {
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { Id } from '@graphprotocol/grc-20';
|
|
1
2
|
export * as Connect from './connect/index.js';
|
|
2
3
|
export * as Entity from './entity/index.js';
|
|
3
4
|
export * as Identity from './identity/index.js';
|
|
@@ -10,5 +11,6 @@ export * as SpaceInfo from './space-info/index.js';
|
|
|
10
11
|
export * from './store.js';
|
|
11
12
|
export * as StoreConnect from './store-connect.js';
|
|
12
13
|
export * as Type from './type/type.js';
|
|
14
|
+
export * as TypeUtils from './type-utils/type-utils.js';
|
|
13
15
|
export * from './types.js';
|
|
14
16
|
export * as Utils from './utils/index.js';
|
package/src/mapping/Mapping.ts
CHANGED
|
@@ -10,30 +10,30 @@ import { namesAreUnique, toCamelCase, toPascalCase } from './Utils.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export type MappingEntry = {
|
|
12
12
|
/**
|
|
13
|
-
* Array of the `Id
|
|
13
|
+
* Array of the `Id` of the type in the Knowledge Graph.
|
|
14
14
|
* Is an array because a type can belong to multiple spaces/extend multiple types.
|
|
15
15
|
*
|
|
16
16
|
* @since 0.2.0
|
|
17
17
|
*/
|
|
18
|
-
typeIds: Array<Grc20Id
|
|
18
|
+
typeIds: Array<Grc20Id>;
|
|
19
19
|
/**
|
|
20
|
-
* Record of property names to the `Id
|
|
20
|
+
* Record of property names to the `Id` of the type in the Knowledge Graph
|
|
21
21
|
*
|
|
22
22
|
* @since 0.2.0
|
|
23
23
|
*/
|
|
24
24
|
properties?:
|
|
25
25
|
| {
|
|
26
|
-
[key: string]: Grc20Id
|
|
26
|
+
[key: string]: Grc20Id;
|
|
27
27
|
}
|
|
28
28
|
| undefined;
|
|
29
29
|
/**
|
|
30
|
-
* Record of relation properties to the `Id
|
|
30
|
+
* Record of relation properties to the `Id` of the type in the Knowledge Graph
|
|
31
31
|
*
|
|
32
32
|
* @since 0.2.0
|
|
33
33
|
*/
|
|
34
34
|
relations?:
|
|
35
35
|
| {
|
|
36
|
-
[key: string]: Grc20Id
|
|
36
|
+
[key: string]: Grc20Id;
|
|
37
37
|
}
|
|
38
38
|
| undefined;
|
|
39
39
|
};
|
|
@@ -46,20 +46,20 @@ export type MappingEntry = {
|
|
|
46
46
|
*
|
|
47
47
|
* const mapping: Mapping = {
|
|
48
48
|
* Account: {
|
|
49
|
-
* typeIds: [Id
|
|
49
|
+
* typeIds: [Id('a5fd07b1-120f-46c6-b46f-387ef98396a6')],
|
|
50
50
|
* properties: {
|
|
51
|
-
* username: Id
|
|
52
|
-
* createdAt: Id
|
|
51
|
+
* username: Id('994edcff-6996-4a77-9797-a13e5e3efad8'),
|
|
52
|
+
* createdAt: Id('64bfba51-a69b-4746-be4b-213214a879fe')
|
|
53
53
|
* }
|
|
54
54
|
* },
|
|
55
55
|
* Event: {
|
|
56
|
-
* typeIds: [Id
|
|
56
|
+
* typeIds: [Id('0349187b-526f-435f-b2bb-9e9caf23127a')],
|
|
57
57
|
* properties: {
|
|
58
|
-
* name: Id
|
|
59
|
-
* description: Id
|
|
58
|
+
* name: Id('3808e060-fb4a-4d08-8069-35b8c8a1902b'),
|
|
59
|
+
* description: Id('1f0d9007-8da2-4b28-ab9f-3bc0709f4837'),
|
|
60
60
|
* },
|
|
61
61
|
* relations: {
|
|
62
|
-
* speaker: Id
|
|
62
|
+
* speaker: Id('a5fd07b1-120f-46c6-b46f-387ef98396a6')
|
|
63
63
|
* }
|
|
64
64
|
* }
|
|
65
65
|
* }
|
|
@@ -94,11 +94,17 @@ export type SchemaDataTypeRelation = typeof SchemaDataTypeRelation.Type;
|
|
|
94
94
|
/**
|
|
95
95
|
* @since 0.2.0
|
|
96
96
|
*/
|
|
97
|
-
export const SchemaDataTypePrimitive = EffectSchema.Literal('
|
|
97
|
+
export const SchemaDataTypePrimitive = EffectSchema.Literal('String', 'Number', 'Boolean', 'Date', 'Point');
|
|
98
98
|
/**
|
|
99
99
|
* @since 0.2.0
|
|
100
100
|
*/
|
|
101
101
|
export type SchemaDataTypePrimitive = typeof SchemaDataTypePrimitive.Type;
|
|
102
|
+
/**
|
|
103
|
+
* @since 0.4.0
|
|
104
|
+
*/
|
|
105
|
+
export function isDataTypePrimitive(val: string): val is SchemaDataTypePrimitive {
|
|
106
|
+
return ['String', 'Number', 'Boolean', 'Date', 'Point'].includes(val);
|
|
107
|
+
}
|
|
102
108
|
/**
|
|
103
109
|
* @since 0.2.0
|
|
104
110
|
*/
|
|
@@ -107,6 +113,22 @@ export const SchemaDataType = EffectSchema.Union(SchemaDataTypePrimitive, Schema
|
|
|
107
113
|
* @since 0.2.0
|
|
108
114
|
*/
|
|
109
115
|
export type SchemaDataType = typeof SchemaDataType.Type;
|
|
116
|
+
/**
|
|
117
|
+
* @since 0.4.0
|
|
118
|
+
*/
|
|
119
|
+
export function isDataType(val: string): val is SchemaDataType {
|
|
120
|
+
return isDataTypePrimitive(val) || isDataTypeRelation(val);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @since 0.4.0
|
|
124
|
+
*/
|
|
125
|
+
export function getDataType(val: string): SchemaDataType {
|
|
126
|
+
const dataType = isDataTypePrimitive(val) || isDataTypeRelation(val);
|
|
127
|
+
if (dataType) {
|
|
128
|
+
return val;
|
|
129
|
+
}
|
|
130
|
+
throw new Error(`Passed dataType ${val} is not supported`);
|
|
131
|
+
}
|
|
110
132
|
/**
|
|
111
133
|
* @since 0.2.0
|
|
112
134
|
*/
|
|
@@ -195,7 +217,7 @@ export const Schema = EffectSchema.Struct({
|
|
|
195
217
|
{
|
|
196
218
|
name: 'Account',
|
|
197
219
|
knowledgeGraphId: null,
|
|
198
|
-
properties: [{ name: 'username', knowledgeGraphId: null, dataType: '
|
|
220
|
+
properties: [{ name: 'username', knowledgeGraphId: null, dataType: 'String' }],
|
|
199
221
|
},
|
|
200
222
|
],
|
|
201
223
|
},
|
|
@@ -204,7 +226,7 @@ export const Schema = EffectSchema.Struct({
|
|
|
204
226
|
{
|
|
205
227
|
name: 'Account',
|
|
206
228
|
knowledgeGraphId: 'a5fd07b1-120f-46c6-b46f-387ef98396a6',
|
|
207
|
-
properties: [{ name: 'name', knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935', dataType: '
|
|
229
|
+
properties: [{ name: 'name', knowledgeGraphId: 'a126ca53-0c8e-48d5-b888-82c734c38935', dataType: 'String' }],
|
|
208
230
|
},
|
|
209
231
|
],
|
|
210
232
|
},
|
|
@@ -238,7 +260,7 @@ export const SchemaUnknownDecoder = EffectSchema.decodeUnknownSync(Schema);
|
|
|
238
260
|
* properties: [
|
|
239
261
|
* {
|
|
240
262
|
* name: "username",
|
|
241
|
-
* dataType: "
|
|
263
|
+
* dataType: "String",
|
|
242
264
|
* knowledgeGraphId: null
|
|
243
265
|
* }
|
|
244
266
|
* ]
|
|
@@ -297,8 +319,8 @@ export function allRelationPropertyTypesExist(types: ReadonlyArray<SchemaType>):
|
|
|
297
319
|
export type GenerateMappingResult = [mapping: Mapping, ops: ReadonlyArray<Op>];
|
|
298
320
|
|
|
299
321
|
// Helper types for internal processing
|
|
300
|
-
type PropertyIdMapping = { propName: string; id: Grc20Id
|
|
301
|
-
type TypeIdMapping = Map<string, Grc20Id
|
|
322
|
+
type PropertyIdMapping = { propName: string; id: Grc20Id };
|
|
323
|
+
type TypeIdMapping = Map<string, Grc20Id | null>;
|
|
302
324
|
type ProcessedProperty =
|
|
303
325
|
| { type: 'resolved'; mapping: PropertyIdMapping; ops: Array<Op> }
|
|
304
326
|
| { type: 'deferred'; property: SchemaTypePropertyRelation };
|
|
@@ -342,7 +364,7 @@ function createPropertyWithOps(
|
|
|
342
364
|
if (property.knowledgeGraphId) {
|
|
343
365
|
return {
|
|
344
366
|
type: 'resolved',
|
|
345
|
-
mapping: { propName: property.name, id: Grc20Id
|
|
367
|
+
mapping: { propName: property.name, id: Grc20Id(property.knowledgeGraphId) },
|
|
346
368
|
ops: [],
|
|
347
369
|
};
|
|
348
370
|
}
|
|
@@ -421,7 +443,7 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
421
443
|
if (type.knowledgeGraphId) {
|
|
422
444
|
const entry: MappingEntry & { typeName: string } = {
|
|
423
445
|
typeName: toPascalCase(type.name),
|
|
424
|
-
typeIds: [Grc20Id
|
|
446
|
+
typeIds: [Grc20Id(type.knowledgeGraphId)],
|
|
425
447
|
};
|
|
426
448
|
|
|
427
449
|
if (EffectArray.isNonEmptyArray(primitiveProperties)) {
|
|
@@ -487,7 +509,7 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
487
509
|
* @example
|
|
488
510
|
* ```ts
|
|
489
511
|
* import { Id } from "@graphprotocol/grc-20"
|
|
490
|
-
* import { generateMapping } from "@graphprotocol/
|
|
512
|
+
* import { generateMapping } from "@graphprotocol/hypergraph"
|
|
491
513
|
*
|
|
492
514
|
* const schema: Schema = {
|
|
493
515
|
* types: [
|
|
@@ -497,7 +519,7 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
497
519
|
* properties: [
|
|
498
520
|
* {
|
|
499
521
|
* name: "username",
|
|
500
|
-
* dataType: "
|
|
522
|
+
* dataType: "String",
|
|
501
523
|
* knowledgeGraphId: "994edcff-6996-4a77-9797-a13e5e3efad8"
|
|
502
524
|
* },
|
|
503
525
|
* {
|
|
@@ -513,12 +535,12 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
513
535
|
* properties: [
|
|
514
536
|
* {
|
|
515
537
|
* name: "name",
|
|
516
|
-
* dataType: "
|
|
538
|
+
* dataType: "String",
|
|
517
539
|
* knowledgeGraphId: "3808e060-fb4a-4d08-8069-35b8c8a1902b"
|
|
518
540
|
* },
|
|
519
541
|
* {
|
|
520
542
|
* name: "description",
|
|
521
|
-
* dataType: "
|
|
543
|
+
* dataType: "String",
|
|
522
544
|
* knowledgeGraphId: null
|
|
523
545
|
* },
|
|
524
546
|
* {
|
|
@@ -535,20 +557,20 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
535
557
|
*
|
|
536
558
|
* expect(mapping).toEqual({
|
|
537
559
|
* Account: {
|
|
538
|
-
* typeIds: [Id
|
|
560
|
+
* typeIds: [Id("a5fd07b1-120f-46c6-b46f-387ef98396a6")], // comes from input schema
|
|
539
561
|
* properties: {
|
|
540
|
-
* username: Id
|
|
541
|
-
* createdAt: Id
|
|
562
|
+
* username: Id("994edcff-6996-4a77-9797-a13e5e3efad8"), // comes from input schema
|
|
563
|
+
* createdAt: Id("8cd7d9ac-a878-4287-8000-e71e6f853117"), // generated from Graph.createProperty Op
|
|
542
564
|
* }
|
|
543
565
|
* },
|
|
544
566
|
* Event: {
|
|
545
|
-
* typeIds: [Id
|
|
567
|
+
* typeIds: [Id("20b3fe39-8e62-41a0-b9cb-92743fd760da")], // generated from Graph.createType Op
|
|
546
568
|
* properties: {
|
|
547
|
-
* name: Id
|
|
548
|
-
* description: Id
|
|
569
|
+
* name: Id("3808e060-fb4a-4d08-8069-35b8c8a1902b"), // comes from input schema
|
|
570
|
+
* description: Id("8fc4e17c-7581-4d6c-a712-943385afc7b5"), // generated from Graph.createProperty Op
|
|
549
571
|
* },
|
|
550
572
|
* relations: {
|
|
551
|
-
* speaker: Id
|
|
573
|
+
* speaker: Id("651ce59f-643b-4931-bf7a-5dc0ca0f5a47"), // generated from Graph.createProperty Op
|
|
552
574
|
* }
|
|
553
575
|
* }
|
|
554
576
|
* })
|
|
@@ -557,23 +579,23 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
557
579
|
* {
|
|
558
580
|
* type: "CREATE_PROPERTY",
|
|
559
581
|
* property: {
|
|
560
|
-
* id: Id
|
|
561
|
-
* dataType: "
|
|
582
|
+
* id: Id("8cd7d9ac-a878-4287-8000-e71e6f853117"),
|
|
583
|
+
* dataType: "String"
|
|
562
584
|
* }
|
|
563
585
|
* },
|
|
564
586
|
* // Graph.createProperty Op for Event.description property
|
|
565
587
|
* {
|
|
566
588
|
* type: "CREATE_PROPERTY",
|
|
567
589
|
* property: {
|
|
568
|
-
* id: Id
|
|
569
|
-
* dataType: "
|
|
590
|
+
* id: Id("8fc4e17c-7581-4d6c-a712-943385afc7b5"),
|
|
591
|
+
* dataType: "String"
|
|
570
592
|
* }
|
|
571
593
|
* },
|
|
572
594
|
* // Graph.createProperty Op for Event.speaker property
|
|
573
595
|
* {
|
|
574
596
|
* type: "CREATE_PROPERTY",
|
|
575
597
|
* property: {
|
|
576
|
-
* id: Id
|
|
598
|
+
* id: Id("651ce59f-643b-4931-bf7a-5dc0ca0f5a47"),
|
|
577
599
|
* dataType: "RELATION"
|
|
578
600
|
* }
|
|
579
601
|
* },
|
|
@@ -581,7 +603,7 @@ function processType(type: SchemaType, typeIdMap: TypeIdMapping): ProcessedType
|
|
|
581
603
|
* {
|
|
582
604
|
* type: "CREATE_PROPERTY",
|
|
583
605
|
* property: {
|
|
584
|
-
* id: Id
|
|
606
|
+
* id: Id("651ce59f-643b-4931-bf7a-5dc0ca0f5a47"),
|
|
585
607
|
* dataType: "RELATION"
|
|
586
608
|
* }
|
|
587
609
|
* },
|
|
@@ -600,8 +622,8 @@ export function generateMapping(input: Schema): GenerateMappingResult {
|
|
|
600
622
|
// Build initial type ID map
|
|
601
623
|
const typeIdMap: TypeIdMapping = pipe(
|
|
602
624
|
schema.types,
|
|
603
|
-
EffectArray.reduce(new Map<string, Grc20Id
|
|
604
|
-
map.set(type.name, type.knowledgeGraphId != null ? Grc20Id
|
|
625
|
+
EffectArray.reduce(new Map<string, Grc20Id | null>(), (map, type) =>
|
|
626
|
+
map.set(type.name, type.knowledgeGraphId != null ? Grc20Id(type.knowledgeGraphId) : null),
|
|
605
627
|
),
|
|
606
628
|
);
|
|
607
629
|
|
|
@@ -749,8 +771,8 @@ export class RelationValueTypeDoesNotExistError extends Data.TaggedError(
|
|
|
749
771
|
*/
|
|
750
772
|
export function mapSchemaDataTypeToGRC20PropDataType(dataType: SchemaDataType): CreatePropertyParams['dataType'] {
|
|
751
773
|
switch (true) {
|
|
752
|
-
case dataType === '
|
|
753
|
-
return '
|
|
774
|
+
case dataType === 'Boolean': {
|
|
775
|
+
return 'BOOLEAN';
|
|
754
776
|
}
|
|
755
777
|
case dataType === 'Date': {
|
|
756
778
|
return 'TIME';
|
|
@@ -765,7 +787,7 @@ export function mapSchemaDataTypeToGRC20PropDataType(dataType: SchemaDataType):
|
|
|
765
787
|
return 'RELATION';
|
|
766
788
|
}
|
|
767
789
|
default: {
|
|
768
|
-
return '
|
|
790
|
+
return 'STRING';
|
|
769
791
|
}
|
|
770
792
|
}
|
|
771
793
|
}
|
package/src/type/type.ts
CHANGED
|
@@ -2,10 +2,12 @@ import * as Schema from 'effect/Schema';
|
|
|
2
2
|
import { Field } from '../entity/entity.js';
|
|
3
3
|
import type { AnyNoContext, EntityWithRelation } from '../entity/types.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
|
|
6
|
+
export const String = Schema.String;
|
|
6
7
|
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
|
|
7
8
|
export const Number = Schema.Number;
|
|
8
|
-
|
|
9
|
+
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
|
|
10
|
+
export const Boolean = Schema.Boolean;
|
|
9
11
|
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
|
|
10
12
|
export const Date = Schema.Date;
|
|
11
13
|
export const Point = Schema.transform(Schema.String, Schema.Array(Number), {
|
|
@@ -16,6 +18,8 @@ export const Point = Schema.transform(Schema.String, Schema.Array(Number), {
|
|
|
16
18
|
encode: (points: readonly number[]) => points.join(','),
|
|
17
19
|
});
|
|
18
20
|
|
|
21
|
+
export const optional = Schema.optional;
|
|
22
|
+
|
|
19
23
|
export const Relation = <S extends AnyNoContext>(schema: S) => {
|
|
20
24
|
const relationSchema = Field({
|
|
21
25
|
select: Schema.Array(schema) as unknown as Schema.Schema<ReadonlyArray<EntityWithRelation<S>>>,
|