@kubun/graphql 0.1.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/LICENSE.md +57 -0
- package/README.md +11 -0
- package/lib/identifiers.d.ts +9 -0
- package/lib/identifiers.d.ts.map +1 -0
- package/lib/identifiers.js +78 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +3 -0
- package/lib/scalars.d.ts +17 -0
- package/lib/scalars.d.ts.map +1 -0
- package/lib/scalars.js +16 -0
- package/lib/schema.d.ts +62 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +718 -0
- package/package.json +39 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# The Prosperity Public License 3.0.0
|
|
2
|
+
|
|
3
|
+
Contributor: Paul Le Cam
|
|
4
|
+
|
|
5
|
+
Source Code: https://github.com/PaulLeCam/kubun
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
This license allows you to use and share this software for noncommercial purposes for free and to try this software for commercial purposes for thirty days.
|
|
10
|
+
|
|
11
|
+
## Agreement
|
|
12
|
+
|
|
13
|
+
In order to receive this license, you have to agree to its rules. Those rules are both obligations under that agreement and conditions to your license. Don't do anything with this software that triggers a rule you can't or won't follow.
|
|
14
|
+
|
|
15
|
+
## Notices
|
|
16
|
+
|
|
17
|
+
Make sure everyone who gets a copy of any part of this software from you, with or without changes, also gets the text of this license and the contributor and source code lines above.
|
|
18
|
+
|
|
19
|
+
## Commercial Trial
|
|
20
|
+
|
|
21
|
+
Limit your use of this software for commercial purposes to a thirty-day trial period. If you use this software for work, your company gets one trial period for all personnel, not one trial per person.
|
|
22
|
+
|
|
23
|
+
## Contributions Back
|
|
24
|
+
|
|
25
|
+
Developing feedback, changes, or additions that you contribute back to the contributor on the terms of a standardized public software license such as [the Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0), [the Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html), [the MIT license](https://spdx.org/licenses/MIT.html), or [the two-clause BSD license](https://spdx.org/licenses/BSD-2-Clause.html) doesn't count as use for a commercial purpose.
|
|
26
|
+
|
|
27
|
+
## Personal Uses
|
|
28
|
+
|
|
29
|
+
Personal use for research, experiment, and testing for the benefit of public knowledge, personal study, private entertainment, hobby projects, amateur pursuits, or religious observance, without any anticipated commercial application, doesn't count as use for a commercial purpose.
|
|
30
|
+
|
|
31
|
+
## Noncommercial Organizations
|
|
32
|
+
|
|
33
|
+
Use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution doesn't count as use for a commercial purpose regardless of the source of funding or obligations resulting from the funding.
|
|
34
|
+
|
|
35
|
+
## Defense
|
|
36
|
+
|
|
37
|
+
Don't make any legal claim against anyone accusing this software, with or without changes, alone or with other technology, of infringing any patent.
|
|
38
|
+
|
|
39
|
+
## Copyright
|
|
40
|
+
|
|
41
|
+
The contributor licenses you to do everything with this software that would otherwise infringe their copyright in it.
|
|
42
|
+
|
|
43
|
+
## Patent
|
|
44
|
+
|
|
45
|
+
The contributor licenses you to do everything with this software that would otherwise infringe any patents they can license or become able to license.
|
|
46
|
+
|
|
47
|
+
## Reliability
|
|
48
|
+
|
|
49
|
+
The contributor can't revoke this license.
|
|
50
|
+
|
|
51
|
+
## Excuse
|
|
52
|
+
|
|
53
|
+
You're excused for unknowingly breaking [Notices](#notices) if you take all practical steps to comply within thirty days of learning you broke the rule.
|
|
54
|
+
|
|
55
|
+
## No Liability
|
|
56
|
+
|
|
57
|
+
***As far as the law allows, this software comes as is, without any warranty or condition, and the contributor won't be liable to anyone for any damages related to this software or this license, under any kind of legal claim.***
|
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GraphQLScalarType } from 'graphql';
|
|
2
|
+
import type { JSONSchema } from 'json-schema-typed';
|
|
3
|
+
export declare const GraphQLCID: GraphQLScalarType<string, string>;
|
|
4
|
+
export declare const cidSchema: JSONSchema.String;
|
|
5
|
+
export declare const GraphQLAttachmentID: GraphQLScalarType<string, string>;
|
|
6
|
+
export declare const attachmentIDSchema: JSONSchema.String;
|
|
7
|
+
export declare const GraphQLDocID: GraphQLScalarType<string, string>;
|
|
8
|
+
export declare const docIDSchema: JSONSchema.String;
|
|
9
|
+
//# sourceMappingURL=identifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,iBAAiB,EAAQ,MAAM,SAAS,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAWnD,eAAO,MAAM,UAAU,mCAYrB,CAAA;AAEF,eAAO,MAAM,SAAS,EAAE,UAAU,CAAC,MAIlC,CAAA;AAWD,eAAO,MAAM,mBAAmB,mCAW9B,CAAA;AAEF,eAAO,MAAM,kBAAkB,EAAE,UAAU,CAAC,MAI3C,CAAA;AAWD,eAAO,MAAM,YAAY,mCAWvB,CAAA;AAEF,eAAO,MAAM,WAAW,EAAE,UAAU,CAAC,MAIpC,CAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AttachmentID, DocumentID } from '@kubun/id';
|
|
2
|
+
import { GraphQLError, GraphQLScalarType, Kind } from 'graphql';
|
|
3
|
+
import { CID } from 'multiformats/cid';
|
|
4
|
+
function validateCID(input) {
|
|
5
|
+
const cid = typeof input === 'string' ? CID.parse(input) : CID.asCID(input);
|
|
6
|
+
if (cid == null) {
|
|
7
|
+
throw new GraphQLError(`Could not parse input as CID: ${input}`);
|
|
8
|
+
}
|
|
9
|
+
return cid.toV1().toString();
|
|
10
|
+
}
|
|
11
|
+
export const GraphQLCID = new GraphQLScalarType({
|
|
12
|
+
name: 'ContentID',
|
|
13
|
+
description: 'A IPLD Content identifier (CID)',
|
|
14
|
+
specifiedByURL: 'https://github.com/multiformats/cid',
|
|
15
|
+
serialize: validateCID,
|
|
16
|
+
parseValue: validateCID,
|
|
17
|
+
parseLiteral (ast) {
|
|
18
|
+
if (ast.kind !== Kind.STRING) {
|
|
19
|
+
throw new GraphQLError(`Can only validate strings as ContentID but got a: ${ast.kind}`);
|
|
20
|
+
}
|
|
21
|
+
return validateCID(ast.value);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
export const cidSchema = {
|
|
25
|
+
type: 'string',
|
|
26
|
+
title: 'cid',
|
|
27
|
+
pattern: '^b[2-7a-z]{99}$'
|
|
28
|
+
};
|
|
29
|
+
function validateAttachmentID(input) {
|
|
30
|
+
if (typeof input !== 'string') {
|
|
31
|
+
throw new GraphQLError(`Invalid AttachmentID input: ${input}`);
|
|
32
|
+
}
|
|
33
|
+
// Throws if input is not a valid AttachmentID string
|
|
34
|
+
AttachmentID.fromString(input);
|
|
35
|
+
return input;
|
|
36
|
+
}
|
|
37
|
+
export const GraphQLAttachmentID = new GraphQLScalarType({
|
|
38
|
+
name: 'AttachmentID',
|
|
39
|
+
description: 'A Kubun Attachment identifier',
|
|
40
|
+
serialize: validateAttachmentID,
|
|
41
|
+
parseValue: validateAttachmentID,
|
|
42
|
+
parseLiteral (ast) {
|
|
43
|
+
if (ast.kind !== Kind.STRING) {
|
|
44
|
+
throw new GraphQLError(`Can only validate strings as AttachmentID but got a: ${ast.kind}`);
|
|
45
|
+
}
|
|
46
|
+
return validateAttachmentID(ast.value);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
export const attachmentIDSchema = {
|
|
50
|
+
type: 'string',
|
|
51
|
+
title: 'attachmentid',
|
|
52
|
+
pattern: '^k[0-9a-z]{99}$'
|
|
53
|
+
};
|
|
54
|
+
function validateDocID(input) {
|
|
55
|
+
if (typeof input !== 'string') {
|
|
56
|
+
throw new GraphQLError(`Invalid DocumentID input: ${input}`);
|
|
57
|
+
}
|
|
58
|
+
// Throws if input is not a valid DocumentID string
|
|
59
|
+
DocumentID.fromString(input);
|
|
60
|
+
return input;
|
|
61
|
+
}
|
|
62
|
+
export const GraphQLDocID = new GraphQLScalarType({
|
|
63
|
+
name: 'DocumentID',
|
|
64
|
+
description: 'A Kubun Document identifier',
|
|
65
|
+
serialize: validateDocID,
|
|
66
|
+
parseValue: validateDocID,
|
|
67
|
+
parseLiteral (ast) {
|
|
68
|
+
if (ast.kind !== Kind.STRING) {
|
|
69
|
+
throw new GraphQLError(`Can only validate strings as DocumentID but got a: ${ast.kind}`);
|
|
70
|
+
}
|
|
71
|
+
return validateDocID(ast.value);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
export const docIDSchema = {
|
|
75
|
+
type: 'string',
|
|
76
|
+
title: 'docid',
|
|
77
|
+
pattern: '^k[0-9a-z]{99}$'
|
|
78
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,EACL,KAAK,YAAY,EACjB,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAA"}
|
package/lib/index.js
ADDED
package/lib/scalars.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JSONSchema } from 'json-schema-typed';
|
|
2
|
+
export { attachmentIDSchema, cidSchema, docIDSchema } from './identifiers.js';
|
|
3
|
+
export declare const bigIntSchema: JSONSchema.String;
|
|
4
|
+
export declare const didSchema: JSONSchema.String;
|
|
5
|
+
export declare const dateTimeSchema: JSONSchema.String;
|
|
6
|
+
export declare const durationSchema: JSONSchema.String;
|
|
7
|
+
export declare const jsonObjectSchema: JSONSchema.String;
|
|
8
|
+
export declare const latitudeSchema: JSONSchema.Number;
|
|
9
|
+
export declare const localDateSchema: JSONSchema.String;
|
|
10
|
+
export declare const localDateTimeSchema: JSONSchema.String;
|
|
11
|
+
export declare const localTimeSchema: JSONSchema.String;
|
|
12
|
+
export declare const localeSchema: JSONSchema.String;
|
|
13
|
+
export declare const longitudeSchema: JSONSchema.Number;
|
|
14
|
+
export declare const timeZoneSchema: JSONSchema.String;
|
|
15
|
+
export declare const urlSchema: JSONSchema.String;
|
|
16
|
+
export declare const utcOffsetSchema: JSONSchema.String;
|
|
17
|
+
//# sourceMappingURL=scalars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scalars.d.ts","sourceRoot":"","sources":["../src/scalars.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE7E,eAAO,MAAM,YAAY,EAA0C,UAAU,CAAC,MAAM,CAAA;AACpF,eAAO,MAAM,SAAS,EAAuC,UAAU,CAAC,MAAM,CAAA;AAC9E,eAAO,MAAM,cAAc,EAA4C,UAAU,CAAC,MAAM,CAAA;AACxF,eAAO,MAAM,cAAc,EAA4C,UAAU,CAAC,MAAM,CAAA;AACxF,eAAO,MAAM,gBAAgB,EAA8C,UAAU,CAAC,MAAM,CAAA;AAC5F,eAAO,MAAM,cAAc,EAA4C,UAAU,CAAC,MAAM,CAAA;AACxF,eAAO,MAAM,eAAe,EAA6C,UAAU,CAAC,MAAM,CAAA;AAC1F,eAAO,MAAM,mBAAmB,EAAiD,UAAU,CAAC,MAAM,CAAA;AAClG,eAAO,MAAM,eAAe,EAA6C,UAAU,CAAC,MAAM,CAAA;AAC1F,eAAO,MAAM,YAAY,EAA0C,UAAU,CAAC,MAAM,CAAA;AACpF,eAAO,MAAM,eAAe,EAA6C,UAAU,CAAC,MAAM,CAAA;AAC1F,eAAO,MAAM,cAAc,EAA4C,UAAU,CAAC,MAAM,CAAA;AACxF,eAAO,MAAM,SAAS,EAAuC,UAAU,CAAC,MAAM,CAAA;AAC9E,eAAO,MAAM,eAAe,EAA6C,UAAU,CAAC,MAAM,CAAA"}
|
package/lib/scalars.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GraphQLBigInt, GraphQLDID, GraphQLDateTime, GraphQLDuration, GraphQLJSONObject, GraphQLLatitude, GraphQLLocalDate, GraphQLLocalDateTime, GraphQLLocalTime, GraphQLLocale, GraphQLLongitude, GraphQLTimeZone, GraphQLURL, GraphQLUtcOffset } from 'graphql-scalars';
|
|
2
|
+
export { attachmentIDSchema, cidSchema, docIDSchema } from './identifiers.js';
|
|
3
|
+
export const bigIntSchema = GraphQLBigInt.extensions.jsonSchema;
|
|
4
|
+
export const didSchema = GraphQLDID.extensions.jsonSchema;
|
|
5
|
+
export const dateTimeSchema = GraphQLDateTime.extensions.jsonSchema;
|
|
6
|
+
export const durationSchema = GraphQLDuration.extensions.jsonSchema;
|
|
7
|
+
export const jsonObjectSchema = GraphQLJSONObject.extensions.jsonSchema;
|
|
8
|
+
export const latitudeSchema = GraphQLLatitude.extensions.jsonSchema;
|
|
9
|
+
export const localDateSchema = GraphQLLocalDate.extensions.jsonSchema;
|
|
10
|
+
export const localDateTimeSchema = GraphQLLocalDateTime.extensions.jsonSchema;
|
|
11
|
+
export const localTimeSchema = GraphQLLocalTime.extensions.jsonSchema;
|
|
12
|
+
export const localeSchema = GraphQLLocale.extensions.jsonSchema;
|
|
13
|
+
export const longitudeSchema = GraphQLLongitude.extensions.jsonSchema;
|
|
14
|
+
export const timeZoneSchema = GraphQLTimeZone.extensions.jsonSchema;
|
|
15
|
+
export const urlSchema = GraphQLURL.extensions.jsonSchema;
|
|
16
|
+
export const utcOffsetSchema = GraphQLUtcOffset.extensions.jsonSchema;
|
package/lib/schema.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { AttachmentID } from '@kubun/id';
|
|
2
|
+
import { type ArrayModel, type DocumentData, type DocumentModel, type DocumentModelSchema, type DocumentModelsRecord, type ObjectModel, type ReferencesRecordModel, type ScalarModel, type StringCustomModel, type StringEnumModel, type StringFormatModel, type StringModel, type TypeModel } from '@kubun/protocol';
|
|
3
|
+
import { GraphQLEnumType, type GraphQLFieldConfig, type GraphQLFieldConfigMap, GraphQLInputObjectType, type GraphQLInputType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, type GraphQLOutputType, type GraphQLScalarType, GraphQLSchema } from 'graphql';
|
|
4
|
+
import { type Connection, type ConnectionArguments } from 'graphql-relay';
|
|
5
|
+
export type DocumentType = {
|
|
6
|
+
id: string;
|
|
7
|
+
model: string;
|
|
8
|
+
owner: string;
|
|
9
|
+
data: DocumentData | null;
|
|
10
|
+
};
|
|
11
|
+
export type ContextType = Record<string, unknown>;
|
|
12
|
+
export type GraphQLNodeDefinitions<Context extends ContextType = ContextType> = {
|
|
13
|
+
nodeInterface: GraphQLInterfaceType;
|
|
14
|
+
nodeField: GraphQLFieldConfig<unknown, Context>;
|
|
15
|
+
nodesField: GraphQLFieldConfig<unknown, Context>;
|
|
16
|
+
};
|
|
17
|
+
export type SharedDefinitions<Document extends DocumentType = DocumentType, Context extends ContextType = ContextType> = GraphQLNodeDefinitions<Context> & {
|
|
18
|
+
accountObject: GraphQLObjectType<string, Context>;
|
|
19
|
+
attachmentObject: GraphQLObjectType<AttachmentID, Context>;
|
|
20
|
+
documentInterface: GraphQLInterfaceType;
|
|
21
|
+
documentMetadata: GraphQLObjectType<Document, Context>;
|
|
22
|
+
queryFields: GraphQLFieldConfigMap<unknown, Context>;
|
|
23
|
+
};
|
|
24
|
+
export type SchemaBuilderParams = {
|
|
25
|
+
aliases?: Record<string, string>;
|
|
26
|
+
record: DocumentModelsRecord;
|
|
27
|
+
};
|
|
28
|
+
export declare class SchemaBuilder<Document extends DocumentType = DocumentType, Context extends ContextType = ContextType, Definitions extends SharedDefinitions<Document, Context> = SharedDefinitions<Document, Context>> {
|
|
29
|
+
_aliases: Record<string, string>;
|
|
30
|
+
_record: DocumentModelsRecord;
|
|
31
|
+
_references: ReferencesRecordModel;
|
|
32
|
+
_relationsTo: Record<string, Record<string, string>>;
|
|
33
|
+
_types: Record<string, GraphQLOutputType>;
|
|
34
|
+
_inputObjects: Record<string, GraphQLInputObjectType>;
|
|
35
|
+
_mutations: Record<string, GraphQLFieldConfig<unknown, Context>>;
|
|
36
|
+
constructor(params: SchemaBuilderParams);
|
|
37
|
+
getViewer(ctx: Context): string | null;
|
|
38
|
+
loadDocument(id: string, ctx: Context): Promise<Document | null>;
|
|
39
|
+
resolveConnection(model: string | null, args: ConnectionArguments, ctx: Context): Promise<Connection<Document>>;
|
|
40
|
+
resolveList(model: string | null, ids: Array<string>, ctx: Context): Promise<Array<Document>>;
|
|
41
|
+
buildMutations(id: string, definitions: Definitions): void;
|
|
42
|
+
build(): GraphQLSchema;
|
|
43
|
+
_getReference(path: string, definitions: Definitions, parentName?: string): GraphQLOutputType;
|
|
44
|
+
_getReferenceInput(path: string, definitions: Definitions, isPartial: boolean, parentName?: string): GraphQLInputType;
|
|
45
|
+
_buildSharedDefinitions(): Definitions;
|
|
46
|
+
_buildDocument(id: string, definitions: Definitions): GraphQLObjectType<Document, Context>;
|
|
47
|
+
_buildDocumentInput(id: string, model: DocumentModel, definitions: Definitions): void;
|
|
48
|
+
_buildReferenceFilterInput(path: string, definitions: Definitions, parentName?: string): GraphQLInputType;
|
|
49
|
+
_buildReferenceOrderByInput(path: string, definitions: Definitions, parentName?: string): GraphQLInputType | null;
|
|
50
|
+
_buildObjectFilterInput(id: string, schema: DocumentModelSchema | ObjectModel, definitions: Definitions, parentName?: string, isDocument?: boolean): GraphQLInputObjectType;
|
|
51
|
+
_buildObjectOrderByInput(id: string, schema: DocumentModelSchema | ObjectModel, definitions: Definitions, parentName?: string, isDocument?: boolean): GraphQLInputObjectType;
|
|
52
|
+
_buildReference(id: string, schema: TypeModel, definitions: Definitions, parentName?: string): GraphQLOutputType;
|
|
53
|
+
_buildScalar(id: string, schema: ScalarModel, definitions: Definitions, asInput?: boolean): GraphQLScalarType;
|
|
54
|
+
_buildArray(schema: ArrayModel, definitions: Definitions, parentName?: string): GraphQLList<GraphQLNonNull<GraphQLOutputType>>;
|
|
55
|
+
_buildObject(id: string, schema: ObjectModel, definitions: Definitions, parentName?: string): GraphQLObjectType;
|
|
56
|
+
_buildObjectFields(id: string, schema: DocumentModelSchema | ObjectModel, definitions: Definitions, model?: DocumentModel): Record<string, GraphQLFieldConfig<Document, Context>>;
|
|
57
|
+
_buildString(schema: StringModel, definitions: Definitions, asInput: boolean): GraphQLEnumType | GraphQLObjectType | GraphQLScalarType;
|
|
58
|
+
_buildCustomString(schema: StringCustomModel, definitions: Definitions, asInput: boolean): GraphQLScalarType<unknown, unknown> | GraphQLObjectType<string, Context> | GraphQLObjectType<AttachmentID, Context>;
|
|
59
|
+
_buildEnumString(schema: StringEnumModel): GraphQLEnumType;
|
|
60
|
+
_buildFormatString(schema: StringFormatModel): GraphQLScalarType<unknown, unknown>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsC,MAAM,WAAW,CAAA;AAC5E,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,WAAW,EAEhB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAEhB,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAEL,eAAe,EAEf,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAI1B,sBAAsB,EACtB,KAAK,gBAAgB,EAErB,oBAAoB,EACpB,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,aAAa,EAKd,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAIzB,MAAM,eAAe,CAAA;AAwGtB,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEjD,MAAM,MAAM,sBAAsB,CAAC,OAAO,SAAS,WAAW,GAAG,WAAW,IAAI;IAC9E,aAAa,EAAE,oBAAoB,CAAA;IACnC,SAAS,EAAE,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC/C,UAAU,EAAE,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;CACjD,CAAA;AACD,MAAM,MAAM,iBAAiB,CAC3B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,WAAW,GAAG,WAAW,IACvC,sBAAsB,CAAC,OAAO,CAAC,GAAG;IACpC,aAAa,EAAE,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjD,gBAAgB,EAAE,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAC1D,iBAAiB,EAAE,oBAAoB,CAAA;IACvC,gBAAgB,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACtD,WAAW,EAAE,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;CACrD,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,MAAM,EAAE,oBAAoB,CAAA;CAC7B,CAAA;AAED,qBAAa,aAAa,CACxB,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,WAAW,GAAG,WAAW,EACzC,WAAW,SAAS,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC;IAG/F,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,OAAO,EAAE,oBAAoB,CAAA;IAC7B,WAAW,EAAE,qBAAqB,CAAK;IACvC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAK;IACzD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAuB;IAChE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAA6B;IAClF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAK;gBAEzD,MAAM,EAAE,mBAAmB;IAqBvC,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAIhC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAIhE,iBAAiB,CACrB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAI1B,WAAW,CACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAI3B,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IAI1D,KAAK,IAAI,aAAa;IAoCtB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB;IAc7F,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,OAAO,EAClB,UAAU,SAAK,GACd,gBAAgB;IA4CnB,uBAAuB,IAAI,WAAW;IAiGtC,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC;IA8B1F,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IA2BrF,0BAA0B,CACxB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,GACd,gBAAgB;IAwCnB,2BAA2B,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,GACd,gBAAgB,GAAG,IAAI;IAiB1B,uBAAuB,CACrB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,mBAAmB,GAAG,WAAW,EACzC,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,EACf,UAAU,UAAQ;IAmCpB,wBAAwB,CACtB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,mBAAmB,GAAG,WAAW,EACzC,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,EACf,UAAU,UAAQ;IA4BpB,eAAe,CACb,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,GACd,iBAAiB;IAiBpB,YAAY,CACV,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,WAAW,EACxB,OAAO,UAAQ,GACd,iBAAiB;IAkBpB,WAAW,CACT,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,GACd,WAAW,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IASjD,YAAY,CACV,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,WAAW,EACxB,UAAU,SAAK,GACd,iBAAiB;IAKpB,kBAAkB,CAChB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,mBAAmB,GAAG,WAAW,EACzC,WAAW,EAAE,WAAW,EACxB,KAAK,CAAC,EAAE,aAAa,GACpB,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IA6ExD,YAAY,CACV,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,GACf,eAAe,GAAG,iBAAiB,GAAG,iBAAiB;IAU1D,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO;IAoBxF,gBAAgB,CAAC,MAAM,EAAE,eAAe;IAQxC,kBAAkB,CAAC,MAAM,EAAE,iBAAiB;CAO7C"}
|
package/lib/schema.js
ADDED
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import { AttachmentID, DocumentID, GraphModelID, decodeID } from '@kubun/id';
|
|
2
|
+
import { REFERENCE_PREFIX } from '@kubun/protocol';
|
|
3
|
+
import { pascalCase } from 'change-case';
|
|
4
|
+
import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLString, isEnumType, isListType, isNonNullType } from 'graphql';
|
|
5
|
+
import { connectionArgs, connectionDefinitions, nodeDefinitions } from 'graphql-relay';
|
|
6
|
+
import { GraphQLDID, GraphQLDuration, GraphQLURL } from 'graphql-scalars';
|
|
7
|
+
import { GraphQLAttachmentID, GraphQLCID, GraphQLDocID } from './identifiers.js';
|
|
8
|
+
const STRING_FORMATS = {
|
|
9
|
+
date: GraphQLString,
|
|
10
|
+
time: GraphQLString,
|
|
11
|
+
'date-time': GraphQLString,
|
|
12
|
+
duration: GraphQLDuration,
|
|
13
|
+
uri: GraphQLURL
|
|
14
|
+
};
|
|
15
|
+
const STRING_TITLES = {
|
|
16
|
+
attachmentid: GraphQLAttachmentID,
|
|
17
|
+
cid: GraphQLCID,
|
|
18
|
+
did: GraphQLDID,
|
|
19
|
+
docid: GraphQLDocID
|
|
20
|
+
};
|
|
21
|
+
function isList(type) {
|
|
22
|
+
return isListType(type) || isNonNullType(type) && isListType(type.ofType);
|
|
23
|
+
}
|
|
24
|
+
function toList(type) {
|
|
25
|
+
return new GraphQLList(new GraphQLNonNull(type));
|
|
26
|
+
}
|
|
27
|
+
function toOuputList(type) {
|
|
28
|
+
return new GraphQLList(new GraphQLNonNull(type));
|
|
29
|
+
}
|
|
30
|
+
function createEnumValueFilterInput(type) {
|
|
31
|
+
return new GraphQLInputObjectType({
|
|
32
|
+
name: `${type.name}ValueFilter`,
|
|
33
|
+
isOneOf: true,
|
|
34
|
+
fields: {
|
|
35
|
+
isNull: {
|
|
36
|
+
type: GraphQLBoolean
|
|
37
|
+
},
|
|
38
|
+
equalTo: {
|
|
39
|
+
type
|
|
40
|
+
},
|
|
41
|
+
notEqualTo: {
|
|
42
|
+
type
|
|
43
|
+
},
|
|
44
|
+
in: {
|
|
45
|
+
type: toList(type)
|
|
46
|
+
},
|
|
47
|
+
notIn: {
|
|
48
|
+
type: toList(type)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function createScalarValueFilterInput(type) {
|
|
54
|
+
return new GraphQLInputObjectType({
|
|
55
|
+
name: `${type.name}ValueFilter`,
|
|
56
|
+
isOneOf: true,
|
|
57
|
+
fields: {
|
|
58
|
+
isNull: {
|
|
59
|
+
type: GraphQLBoolean
|
|
60
|
+
},
|
|
61
|
+
equalTo: {
|
|
62
|
+
type
|
|
63
|
+
},
|
|
64
|
+
notEqualTo: {
|
|
65
|
+
type
|
|
66
|
+
},
|
|
67
|
+
in: {
|
|
68
|
+
type: toList(type)
|
|
69
|
+
},
|
|
70
|
+
notIn: {
|
|
71
|
+
type: toList(type)
|
|
72
|
+
},
|
|
73
|
+
lessThan: {
|
|
74
|
+
type
|
|
75
|
+
},
|
|
76
|
+
lessThanOrEqualTo: {
|
|
77
|
+
type
|
|
78
|
+
},
|
|
79
|
+
greaterThan: {
|
|
80
|
+
type
|
|
81
|
+
},
|
|
82
|
+
greaterThanOrEqualTo: {
|
|
83
|
+
type
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const VALUE_FILTER_INPUTS = {
|
|
89
|
+
AccountValueFilter: new GraphQLInputObjectType({
|
|
90
|
+
name: 'AccountValueFilter',
|
|
91
|
+
isOneOf: true,
|
|
92
|
+
fields: {
|
|
93
|
+
equalTo: {
|
|
94
|
+
type: GraphQLDID
|
|
95
|
+
},
|
|
96
|
+
notEqualTo: {
|
|
97
|
+
type: GraphQLDID
|
|
98
|
+
},
|
|
99
|
+
in: {
|
|
100
|
+
type: toList(GraphQLDID)
|
|
101
|
+
},
|
|
102
|
+
notIn: {
|
|
103
|
+
type: toList(GraphQLDID)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}),
|
|
107
|
+
BooleanValueFilter: new GraphQLInputObjectType({
|
|
108
|
+
name: 'BooleanValueFilter',
|
|
109
|
+
isOneOf: true,
|
|
110
|
+
fields: {
|
|
111
|
+
isNull: {
|
|
112
|
+
type: GraphQLBoolean
|
|
113
|
+
},
|
|
114
|
+
equalTo: {
|
|
115
|
+
type: GraphQLBoolean
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
FloatValueFilter: createScalarValueFilterInput(GraphQLFloat),
|
|
120
|
+
IntValueFilter: createScalarValueFilterInput(GraphQLInt),
|
|
121
|
+
StringValueFilter: createScalarValueFilterInput(GraphQLString)
|
|
122
|
+
};
|
|
123
|
+
const VALUE_FILTER_INPUT_TYPES = {
|
|
124
|
+
GraphQLBoolean: 'BooleanValueFilter',
|
|
125
|
+
GraphQLDID: 'AccountValueFilter',
|
|
126
|
+
GraphQLFloat: 'FloatValueFilter',
|
|
127
|
+
GraphQLInt: 'IntValueFilter',
|
|
128
|
+
GraphQLString: 'StringValueFilter'
|
|
129
|
+
};
|
|
130
|
+
const OrderByDirection = new GraphQLEnumType({
|
|
131
|
+
name: 'OrderByDirection',
|
|
132
|
+
values: {
|
|
133
|
+
ASC: {
|
|
134
|
+
value: 'asc'
|
|
135
|
+
},
|
|
136
|
+
DESC: {
|
|
137
|
+
value: 'desc'
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
export class SchemaBuilder {
|
|
142
|
+
// Mapping of model or references IDs to their GraphQL object names
|
|
143
|
+
_aliases;
|
|
144
|
+
_record;
|
|
145
|
+
_references = {};
|
|
146
|
+
_relationsTo = {};
|
|
147
|
+
_types = {
|
|
148
|
+
OrderByDirection
|
|
149
|
+
};
|
|
150
|
+
_inputObjects = {
|
|
151
|
+
...VALUE_FILTER_INPUTS
|
|
152
|
+
};
|
|
153
|
+
_mutations = {};
|
|
154
|
+
constructor(params){
|
|
155
|
+
this._aliases = params.aliases ?? {};
|
|
156
|
+
this._record = params.record;
|
|
157
|
+
for (const [id, model] of Object.entries(params.record)){
|
|
158
|
+
const modelID = GraphModelID.fromString(id);
|
|
159
|
+
Object.assign(this._references, model.schema.$defs ?? {});
|
|
160
|
+
// Extract inverse relations
|
|
161
|
+
for (const [fieldName, meta] of Object.entries(model.fieldsMeta)){
|
|
162
|
+
if (meta.relationModel != null) {
|
|
163
|
+
const relationModelID = GraphModelID.fromString(meta.relationModel);
|
|
164
|
+
const globalID = relationModelID.isLocal ? relationModelID.toGlobal(modelID) : relationModelID;
|
|
165
|
+
const relationID = globalID.toString();
|
|
166
|
+
this._relationsTo[relationID] ??= {};
|
|
167
|
+
this._relationsTo[relationID][fieldName] = id;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
getViewer(ctx) {
|
|
173
|
+
throw new Error('Must be implemented');
|
|
174
|
+
}
|
|
175
|
+
async loadDocument(id, ctx) {
|
|
176
|
+
throw new Error('Must be implemented');
|
|
177
|
+
}
|
|
178
|
+
async resolveConnection(model, args, ctx) {
|
|
179
|
+
throw new Error('Must be implemented');
|
|
180
|
+
}
|
|
181
|
+
async resolveList(model, ids, ctx) {
|
|
182
|
+
throw new Error('Must be implemented');
|
|
183
|
+
}
|
|
184
|
+
buildMutations(id, definitions) {
|
|
185
|
+
throw new Error('Must be implemented');
|
|
186
|
+
}
|
|
187
|
+
build() {
|
|
188
|
+
const definitions = this._buildSharedDefinitions();
|
|
189
|
+
const queryFields = {
|
|
190
|
+
nodes: definitions.nodesField,
|
|
191
|
+
...definitions.queryFields
|
|
192
|
+
};
|
|
193
|
+
// TODO: is this needed?
|
|
194
|
+
// for (const [id, schema] of Object.entries(this._references)) {
|
|
195
|
+
// this._buildReference(id, schema, definitions)
|
|
196
|
+
// }
|
|
197
|
+
for (const id of Object.keys(this._record)){
|
|
198
|
+
this._buildDocument(id, definitions);
|
|
199
|
+
this.buildMutations(id, definitions);
|
|
200
|
+
const name = this._aliases[id];
|
|
201
|
+
queryFields[`list${name}`] = {
|
|
202
|
+
type: this._types[`${id}-connection`],
|
|
203
|
+
args: {
|
|
204
|
+
...connectionArgs,
|
|
205
|
+
filter: {
|
|
206
|
+
type: this._inputObjects[`${id}-filter`]
|
|
207
|
+
},
|
|
208
|
+
orderBy: {
|
|
209
|
+
type: toList(this._inputObjects[`${id}-order`])
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
resolve: async (_, args, ctx)=>{
|
|
213
|
+
return await this.resolveConnection(id, args, ctx);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const schemaFields = {
|
|
218
|
+
query: new GraphQLObjectType({
|
|
219
|
+
name: 'Query',
|
|
220
|
+
fields: queryFields
|
|
221
|
+
}),
|
|
222
|
+
mutation: new GraphQLObjectType({
|
|
223
|
+
name: 'Mutation',
|
|
224
|
+
fields: this._mutations
|
|
225
|
+
})
|
|
226
|
+
};
|
|
227
|
+
return new GraphQLSchema(schemaFields);
|
|
228
|
+
}
|
|
229
|
+
_getReference(path, definitions, parentName) {
|
|
230
|
+
const id = path.slice(REFERENCE_PREFIX.length);
|
|
231
|
+
const type = this._types[id];
|
|
232
|
+
if (type != null) {
|
|
233
|
+
return type;
|
|
234
|
+
}
|
|
235
|
+
const schema = this._references[id];
|
|
236
|
+
if (schema == null) {
|
|
237
|
+
throw new Error(`Could not find reference: ${id}`);
|
|
238
|
+
}
|
|
239
|
+
return this._buildReference(id, schema, definitions, parentName);
|
|
240
|
+
}
|
|
241
|
+
_getReferenceInput(path, definitions, isPartial, parentName = '') {
|
|
242
|
+
const id = path.slice(REFERENCE_PREFIX.length);
|
|
243
|
+
const schema = this._references[id];
|
|
244
|
+
if (schema == null) {
|
|
245
|
+
throw new Error(`Could not find reference: ${id}`);
|
|
246
|
+
}
|
|
247
|
+
if (schema.type === 'array') {
|
|
248
|
+
const itemType = this._getReferenceInput(schema.items.$ref, definitions, false, schema.title);
|
|
249
|
+
return new GraphQLList(new GraphQLNonNull(itemType));
|
|
250
|
+
}
|
|
251
|
+
if (schema.type === 'object') {
|
|
252
|
+
const inputID = isPartial ? `${id}-partial` : id;
|
|
253
|
+
const existing = this._inputObjects[inputID];
|
|
254
|
+
if (existing != null) {
|
|
255
|
+
return existing;
|
|
256
|
+
}
|
|
257
|
+
const fields = {};
|
|
258
|
+
for (const [key, value] of Object.entries(schema.properties)){
|
|
259
|
+
const type = this._getReferenceInput(value.$ref, definitions, isPartial, schema.title);
|
|
260
|
+
fields[key] = {
|
|
261
|
+
type: isPartial || !schema.required.includes(key) ? type : new GraphQLNonNull(type)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const name = this._aliases[id] ?? pascalCase(parentName + schema.title);
|
|
265
|
+
const input = new GraphQLInputObjectType({
|
|
266
|
+
name: isPartial ? `Partial${name}Input` : `${name}Input`,
|
|
267
|
+
fields
|
|
268
|
+
});
|
|
269
|
+
this._inputObjects[inputID] = input;
|
|
270
|
+
return input;
|
|
271
|
+
}
|
|
272
|
+
return this._buildScalar(id, schema, definitions, true);
|
|
273
|
+
}
|
|
274
|
+
_buildSharedDefinitions() {
|
|
275
|
+
const nodeDefs = nodeDefinitions(async (id, ctx)=>{
|
|
276
|
+
if (id.startsWith('did:')) {
|
|
277
|
+
return id;
|
|
278
|
+
}
|
|
279
|
+
const decoded = decodeID(id);
|
|
280
|
+
if (decoded instanceof AttachmentID) {
|
|
281
|
+
return decoded;
|
|
282
|
+
}
|
|
283
|
+
if (decoded instanceof DocumentID) {
|
|
284
|
+
return await this.loadDocument(id, ctx);
|
|
285
|
+
}
|
|
286
|
+
throw new Error(`Unsupported node ID: ${id}`);
|
|
287
|
+
}, (node)=>{
|
|
288
|
+
return typeof node === 'string' ? 'AccountNode' : node instanceof AttachmentID ? 'AttachmentNode' : this._aliases[node.model];
|
|
289
|
+
});
|
|
290
|
+
const accountObject = new GraphQLObjectType({
|
|
291
|
+
name: 'AccountNode',
|
|
292
|
+
interfaces: [
|
|
293
|
+
nodeDefs.nodeInterface
|
|
294
|
+
],
|
|
295
|
+
fields: ()=>{
|
|
296
|
+
const config = {
|
|
297
|
+
id: {
|
|
298
|
+
type: new GraphQLNonNull(GraphQLID),
|
|
299
|
+
description: 'Globally unique identifier of the account (DID string)',
|
|
300
|
+
resolve: (did)=>did
|
|
301
|
+
},
|
|
302
|
+
isViewer: {
|
|
303
|
+
type: new GraphQLNonNull(GraphQLBoolean),
|
|
304
|
+
description: 'Whether the Ceramic instance is currently authenticated with this account or not',
|
|
305
|
+
resolve: (did, _, ctx)=>this.getViewer(ctx) === did
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
// TODO: add entry-points to documents created with this account
|
|
309
|
+
return config;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
const attachmentObject = new GraphQLObjectType({
|
|
313
|
+
name: 'AttachmentNode',
|
|
314
|
+
interfaces: [
|
|
315
|
+
nodeDefs.nodeInterface
|
|
316
|
+
],
|
|
317
|
+
fields: {
|
|
318
|
+
id: {
|
|
319
|
+
type: new GraphQLNonNull(GraphQLID),
|
|
320
|
+
resolve: (aid)=>aid.toString()
|
|
321
|
+
},
|
|
322
|
+
cid: {
|
|
323
|
+
type: new GraphQLNonNull(GraphQLCID)
|
|
324
|
+
},
|
|
325
|
+
contentLength: {
|
|
326
|
+
type: new GraphQLNonNull(GraphQLInt)
|
|
327
|
+
},
|
|
328
|
+
mimeType: {
|
|
329
|
+
type: new GraphQLNonNull(GraphQLString)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
const documentMetadata = new GraphQLObjectType({
|
|
334
|
+
name: 'DocumentMetadata',
|
|
335
|
+
fields: {
|
|
336
|
+
model: {
|
|
337
|
+
type: new GraphQLNonNull(GraphQLString),
|
|
338
|
+
resolve: (doc)=>doc.model
|
|
339
|
+
},
|
|
340
|
+
owner: {
|
|
341
|
+
type: new GraphQLNonNull(accountObject),
|
|
342
|
+
resolve: (doc)=>doc.owner
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
const documentInterface = new GraphQLInterfaceType({
|
|
347
|
+
name: 'DocumentNode',
|
|
348
|
+
interfaces: [
|
|
349
|
+
nodeDefs.nodeInterface
|
|
350
|
+
],
|
|
351
|
+
fields: {
|
|
352
|
+
// Node interface
|
|
353
|
+
id: {
|
|
354
|
+
type: new GraphQLNonNull(GraphQLID)
|
|
355
|
+
},
|
|
356
|
+
_doc_metadata: {
|
|
357
|
+
type: new GraphQLNonNull(documentMetadata)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const queryFields = {
|
|
362
|
+
node: nodeDefs.nodeField,
|
|
363
|
+
viewer: {
|
|
364
|
+
type: accountObject,
|
|
365
|
+
description: 'Account performing the request, if set',
|
|
366
|
+
resolve: (_self, _args, ctx)=>this.getViewer(ctx)
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
return {
|
|
370
|
+
...nodeDefs,
|
|
371
|
+
accountObject,
|
|
372
|
+
attachmentObject,
|
|
373
|
+
documentInterface,
|
|
374
|
+
documentMetadata,
|
|
375
|
+
queryFields
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
_buildDocument(id, definitions) {
|
|
379
|
+
if (this._types[id] != null) {
|
|
380
|
+
return this._types[id];
|
|
381
|
+
}
|
|
382
|
+
const model = this._record[id];
|
|
383
|
+
if (model == null) {
|
|
384
|
+
throw new Error(`Could not find model id: ${id}`);
|
|
385
|
+
}
|
|
386
|
+
this._aliases[id] ??= pascalCase(model.name);
|
|
387
|
+
const nodeType = new GraphQLObjectType({
|
|
388
|
+
name: this._aliases[id],
|
|
389
|
+
interfaces: [
|
|
390
|
+
definitions.documentInterface,
|
|
391
|
+
definitions.nodeInterface
|
|
392
|
+
],
|
|
393
|
+
fields: ()=>{
|
|
394
|
+
return this._buildObjectFields(id, model.schema, definitions, model);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
const { connectionType, edgeType } = connectionDefinitions({
|
|
398
|
+
nodeType
|
|
399
|
+
});
|
|
400
|
+
this._types[id] = nodeType;
|
|
401
|
+
this._types[`${id}-connection`] = connectionType;
|
|
402
|
+
this._types[`${id}-edge`] = edgeType;
|
|
403
|
+
this._buildDocumentInput(id, model, definitions);
|
|
404
|
+
this._buildObjectFilterInput(id, model.schema, definitions, model.name, true);
|
|
405
|
+
this._buildObjectOrderByInput(id, model.schema, definitions, model.name, true);
|
|
406
|
+
return nodeType;
|
|
407
|
+
}
|
|
408
|
+
_buildDocumentInput(id, model, definitions) {
|
|
409
|
+
if (model.behavior === 'interface') {
|
|
410
|
+
// No mutations for interfaces
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const setFields = {};
|
|
414
|
+
const updateFields = {};
|
|
415
|
+
for (const [key, value] of Object.entries(model.schema.properties)){
|
|
416
|
+
const setType = this._getReferenceInput(value.$ref, definitions, false);
|
|
417
|
+
setFields[key] = {
|
|
418
|
+
type: model.schema.required.includes(key) ? new GraphQLNonNull(setType) : setType
|
|
419
|
+
};
|
|
420
|
+
updateFields[key] = {
|
|
421
|
+
type: this._getReferenceInput(value.$ref, definitions, true)
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const name = this._aliases[id];
|
|
425
|
+
this._inputObjects[id] = new GraphQLInputObjectType({
|
|
426
|
+
name: model.behavior === 'list' ? `Create${name}` : `Set${name}`,
|
|
427
|
+
fields: setFields
|
|
428
|
+
});
|
|
429
|
+
this._inputObjects[`${id}-update`] = new GraphQLInputObjectType({
|
|
430
|
+
name: `Update${name}`,
|
|
431
|
+
fields: updateFields
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
_buildReferenceFilterInput(path, definitions, parentName = '') {
|
|
435
|
+
const id = path.slice(REFERENCE_PREFIX.length);
|
|
436
|
+
const schema = this._references[id];
|
|
437
|
+
if (schema == null) {
|
|
438
|
+
throw new Error(`Could not find reference: ${id}`);
|
|
439
|
+
}
|
|
440
|
+
if (schema.type === 'object') {
|
|
441
|
+
return this._buildObjectFilterInput(id, schema, definitions, parentName);
|
|
442
|
+
}
|
|
443
|
+
if (schema.type === 'array') {
|
|
444
|
+
const name = this._aliases[id] ?? pascalCase(parentName + schema.title);
|
|
445
|
+
const itemType = this._buildReferenceFilterInput(schema.items.$ref, definitions, schema.title);
|
|
446
|
+
return new GraphQLInputObjectType({
|
|
447
|
+
name: `${name}Filter`,
|
|
448
|
+
isOneOf: true,
|
|
449
|
+
fields: {
|
|
450
|
+
contains: {
|
|
451
|
+
type: toList(itemType)
|
|
452
|
+
},
|
|
453
|
+
isEmpty: {
|
|
454
|
+
type: GraphQLBoolean
|
|
455
|
+
},
|
|
456
|
+
isNull: {
|
|
457
|
+
type: GraphQLBoolean
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
const scalar = this._buildScalar(id, schema, definitions, true);
|
|
463
|
+
const knownFilter = VALUE_FILTER_INPUT_TYPES[scalar.name];
|
|
464
|
+
if (knownFilter != null) {
|
|
465
|
+
return this._inputObjects[knownFilter];
|
|
466
|
+
}
|
|
467
|
+
const filter = isEnumType(scalar) ? createEnumValueFilterInput(scalar) : createScalarValueFilterInput(scalar);
|
|
468
|
+
if (this._inputObjects[filter.name] == null) {
|
|
469
|
+
this._inputObjects[filter.name] = filter;
|
|
470
|
+
}
|
|
471
|
+
return this._inputObjects[filter.name];
|
|
472
|
+
}
|
|
473
|
+
_buildReferenceOrderByInput(path, definitions, parentName = '') {
|
|
474
|
+
const id = path.slice(REFERENCE_PREFIX.length);
|
|
475
|
+
const schema = this._references[id];
|
|
476
|
+
if (schema == null) {
|
|
477
|
+
throw new Error(`Could not find reference: ${id}`);
|
|
478
|
+
}
|
|
479
|
+
switch(schema.type){
|
|
480
|
+
case 'array':
|
|
481
|
+
return null;
|
|
482
|
+
case 'object':
|
|
483
|
+
return this._buildObjectOrderByInput(id, schema, definitions, parentName);
|
|
484
|
+
default:
|
|
485
|
+
return OrderByDirection;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
_buildObjectFilterInput(id, schema, definitions, parentName = '', isDocument = false) {
|
|
489
|
+
const inputID = `${id}-filter`;
|
|
490
|
+
const existing = this._inputObjects[inputID];
|
|
491
|
+
if (existing != null) {
|
|
492
|
+
return existing;
|
|
493
|
+
}
|
|
494
|
+
const name = this._aliases[id] ?? pascalCase(parentName + (schema.title ?? ''));
|
|
495
|
+
const fields = isDocument ? {
|
|
496
|
+
_doc_owner: {
|
|
497
|
+
type: this._inputObjects.AccountValueFilter
|
|
498
|
+
}
|
|
499
|
+
} : {};
|
|
500
|
+
for (const [key, value] of Object.entries(schema.properties)){
|
|
501
|
+
const type = this._buildReferenceFilterInput(value.$ref, definitions, name);
|
|
502
|
+
fields[key] = {
|
|
503
|
+
type
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const objectInput = new GraphQLInputObjectType({
|
|
507
|
+
name: `${name}ObjectFilter`,
|
|
508
|
+
fields
|
|
509
|
+
});
|
|
510
|
+
const input = new GraphQLInputObjectType({
|
|
511
|
+
name: `${name}Filter`,
|
|
512
|
+
isOneOf: true,
|
|
513
|
+
fields: ()=>({
|
|
514
|
+
where: {
|
|
515
|
+
type: objectInput
|
|
516
|
+
},
|
|
517
|
+
and: {
|
|
518
|
+
type: toList(this._inputObjects[inputID])
|
|
519
|
+
},
|
|
520
|
+
or: {
|
|
521
|
+
type: toList(this._inputObjects[inputID])
|
|
522
|
+
},
|
|
523
|
+
not: {
|
|
524
|
+
type: this._inputObjects[inputID]
|
|
525
|
+
}
|
|
526
|
+
})
|
|
527
|
+
});
|
|
528
|
+
this._inputObjects[inputID] = input;
|
|
529
|
+
return input;
|
|
530
|
+
}
|
|
531
|
+
_buildObjectOrderByInput(id, schema, definitions, parentName = '', isDocument = false) {
|
|
532
|
+
const inputID = `${id}-order`;
|
|
533
|
+
const existing = this._inputObjects[inputID];
|
|
534
|
+
if (existing != null) {
|
|
535
|
+
return existing;
|
|
536
|
+
}
|
|
537
|
+
const name = this._aliases[id] ?? pascalCase(parentName + (schema.title ?? ''));
|
|
538
|
+
const fields = isDocument ? {
|
|
539
|
+
_doc_owner: {
|
|
540
|
+
type: OrderByDirection
|
|
541
|
+
},
|
|
542
|
+
_doc_created_at: {
|
|
543
|
+
type: OrderByDirection
|
|
544
|
+
}
|
|
545
|
+
} : {};
|
|
546
|
+
for (const [key, value] of Object.entries(schema.properties)){
|
|
547
|
+
const type = this._buildReferenceOrderByInput(value.$ref, definitions, name);
|
|
548
|
+
if (type != null) {
|
|
549
|
+
fields[key] = {
|
|
550
|
+
type
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const input = new GraphQLInputObjectType({
|
|
555
|
+
name: `${name}OrderBy`,
|
|
556
|
+
isOneOf: true,
|
|
557
|
+
fields
|
|
558
|
+
});
|
|
559
|
+
this._inputObjects[inputID] = input;
|
|
560
|
+
return input;
|
|
561
|
+
}
|
|
562
|
+
_buildReference(id, schema, definitions, parentName = '') {
|
|
563
|
+
if (this._types[id] != null) {
|
|
564
|
+
return this._types[id];
|
|
565
|
+
}
|
|
566
|
+
switch(schema.type){
|
|
567
|
+
case 'array':
|
|
568
|
+
this._types[id] = this._buildArray(schema, definitions, parentName);
|
|
569
|
+
return this._types[id];
|
|
570
|
+
case 'object':
|
|
571
|
+
this._types[id] = this._buildObject(id, schema, definitions, parentName);
|
|
572
|
+
return this._types[id];
|
|
573
|
+
default:
|
|
574
|
+
return this._buildScalar(id, schema, definitions);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
_buildScalar(id, schema, definitions, asInput = false) {
|
|
578
|
+
switch(schema.type){
|
|
579
|
+
case 'boolean':
|
|
580
|
+
this._types[id] = GraphQLBoolean;
|
|
581
|
+
break;
|
|
582
|
+
case 'integer':
|
|
583
|
+
this._types[id] = GraphQLInt;
|
|
584
|
+
break;
|
|
585
|
+
case 'number':
|
|
586
|
+
this._types[id] = GraphQLFloat;
|
|
587
|
+
break;
|
|
588
|
+
case 'string':
|
|
589
|
+
this._types[id] = this._buildString(schema, definitions, asInput);
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
return this._types[id];
|
|
593
|
+
}
|
|
594
|
+
_buildArray(schema, definitions, parentName = '') {
|
|
595
|
+
const itemRef = schema.items.$ref;
|
|
596
|
+
return new GraphQLList(new GraphQLNonNull(this._getReference(itemRef, definitions, pascalCase(parentName + schema.title))));
|
|
597
|
+
}
|
|
598
|
+
_buildObject(id, schema, definitions, parentName = '') {
|
|
599
|
+
const fields = this._buildObjectFields(id, schema, definitions);
|
|
600
|
+
return new GraphQLObjectType({
|
|
601
|
+
name: pascalCase(parentName + schema.title),
|
|
602
|
+
fields
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
_buildObjectFields(id, schema, definitions, model) {
|
|
606
|
+
const required = schema.required ?? [];
|
|
607
|
+
const fields = model ? {
|
|
608
|
+
id: {
|
|
609
|
+
type: new GraphQLNonNull(GraphQLID),
|
|
610
|
+
resolve: (doc)=>doc.id
|
|
611
|
+
},
|
|
612
|
+
_doc_metadata: {
|
|
613
|
+
type: new GraphQLNonNull(definitions.documentMetadata),
|
|
614
|
+
resolve: (doc)=>doc
|
|
615
|
+
}
|
|
616
|
+
} : {};
|
|
617
|
+
for (const [key, value] of Object.entries(schema.properties ?? {})){
|
|
618
|
+
const fieldRef = value.$ref;
|
|
619
|
+
if (fieldRef == null) {
|
|
620
|
+
throw new Error(`Missing ref for field ${key} of object ${id}`);
|
|
621
|
+
}
|
|
622
|
+
const type = this._getReference(fieldRef, definitions, schema.title ?? '');
|
|
623
|
+
fields[key] = {
|
|
624
|
+
type: required.includes(key) ? new GraphQLNonNull(type) : type
|
|
625
|
+
};
|
|
626
|
+
if (model != null) {
|
|
627
|
+
fields[key].resolve = (doc)=>doc.data?.[key];
|
|
628
|
+
const relationModel = model.fieldsMeta[key]?.relationModel;
|
|
629
|
+
if (typeof relationModel !== 'undefined') {
|
|
630
|
+
let relationModelID;
|
|
631
|
+
let relationType;
|
|
632
|
+
if (relationModel === null) {
|
|
633
|
+
relationModelID = null;
|
|
634
|
+
relationType = definitions.documentInterface;
|
|
635
|
+
} else {
|
|
636
|
+
const relationID = GraphModelID.fromString(relationModel);
|
|
637
|
+
relationModelID = (relationID.isLocal ? relationID.toGlobal(GraphModelID.fromString(id)) : relationID).toString();
|
|
638
|
+
relationType = this._buildDocument(relationModelID, definitions);
|
|
639
|
+
}
|
|
640
|
+
if (isList(type)) {
|
|
641
|
+
// TODO: add suport for filters?
|
|
642
|
+
fields[`_${key}_documents`] = {
|
|
643
|
+
type: new GraphQLNonNull(toOuputList(relationType)),
|
|
644
|
+
resolve: async (doc, _args, ctx)=>{
|
|
645
|
+
const relationIDs = doc.data?.[key] ?? [];
|
|
646
|
+
return relationIDs.length ? await this.resolveList(relationModelID, relationIDs, ctx) : [];
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
} else {
|
|
650
|
+
fields[`_${key}_document`] = {
|
|
651
|
+
type: relationType,
|
|
652
|
+
resolve: async (doc, _args, ctx)=>{
|
|
653
|
+
const relationID = doc.data?.[key];
|
|
654
|
+
return relationID ? await this.loadDocument(relationID, ctx) : null;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
for (const [fieldName, relationID] of Object.entries(this._relationsTo[id] ?? {})){
|
|
660
|
+
const relationName = this._aliases[relationID];
|
|
661
|
+
fields[`_${fieldName}_of_${relationName}_list`] = {
|
|
662
|
+
type: new GraphQLNonNull(this._types[`${relationID}-connection`])
|
|
663
|
+
};
|
|
664
|
+
fields[`_${fieldName}_of_${relationName}_count`] = {
|
|
665
|
+
type: new GraphQLNonNull(GraphQLInt)
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return fields;
|
|
671
|
+
}
|
|
672
|
+
_buildString(schema, definitions, asInput) {
|
|
673
|
+
if (schema.enum != null) {
|
|
674
|
+
return this._buildEnumString(schema);
|
|
675
|
+
}
|
|
676
|
+
if (schema.format != null) {
|
|
677
|
+
return this._buildFormatString(schema);
|
|
678
|
+
}
|
|
679
|
+
return this._buildCustomString(schema, definitions, asInput);
|
|
680
|
+
}
|
|
681
|
+
_buildCustomString(schema, definitions, asInput) {
|
|
682
|
+
if (schema.title == null) {
|
|
683
|
+
// Plain string type
|
|
684
|
+
return GraphQLString;
|
|
685
|
+
}
|
|
686
|
+
const title = schema.title.toLowerCase();
|
|
687
|
+
if (title === 'attachmentid') {
|
|
688
|
+
return asInput ? GraphQLID : definitions.attachmentObject;
|
|
689
|
+
}
|
|
690
|
+
if (title === 'did') {
|
|
691
|
+
return asInput ? GraphQLID : definitions.accountObject;
|
|
692
|
+
}
|
|
693
|
+
const scalar = STRING_TITLES[title];
|
|
694
|
+
if (scalar == null) {
|
|
695
|
+
throw new Error(`Unsupported string type: ${schema.title}`);
|
|
696
|
+
}
|
|
697
|
+
return scalar;
|
|
698
|
+
}
|
|
699
|
+
_buildEnumString(schema) {
|
|
700
|
+
const values = {};
|
|
701
|
+
for (const value of schema.enum){
|
|
702
|
+
values[value] = {
|
|
703
|
+
value
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
return new GraphQLEnumType({
|
|
707
|
+
name: schema.title,
|
|
708
|
+
values
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
_buildFormatString(schema) {
|
|
712
|
+
const scalar = STRING_FORMATS[schema.format];
|
|
713
|
+
if (scalar == null) {
|
|
714
|
+
throw new Error(`Unsupported string format: ${schema.format}`);
|
|
715
|
+
}
|
|
716
|
+
return scalar;
|
|
717
|
+
}
|
|
718
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kubun/graphql",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "see LICENSE.md",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "lib/index.js",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./lib/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib/*",
|
|
14
|
+
"LICENSE.md"
|
|
15
|
+
],
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"change-case": "^5.4.4",
|
|
19
|
+
"graphql": "^16.9.0",
|
|
20
|
+
"graphql-relay": "^0.10.2",
|
|
21
|
+
"graphql-scalars": "^1.23.0",
|
|
22
|
+
"multiformats": "^13.3.0",
|
|
23
|
+
"@kubun/id": "^0.1.0",
|
|
24
|
+
"@kubun/protocol": "^0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"json-schema-typed": "^8.0.1"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build:clean": "del lib",
|
|
31
|
+
"build:js": "swc src -d ./lib --config-file ../../swc.json --strip-leading-paths",
|
|
32
|
+
"build:types": "tsc --emitDeclarationOnly --skipLibCheck",
|
|
33
|
+
"build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
|
|
34
|
+
"build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
|
|
35
|
+
"test:types": "tsc --noEmit",
|
|
36
|
+
"test:unit": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js",
|
|
37
|
+
"test": "pnpm run test:types && pnpm run test:unit"
|
|
38
|
+
}
|
|
39
|
+
}
|