diff --git a/abi/0.2.json b/abi/0.2.json new file mode 100644 index 0000000..be43df4 --- /dev/null +++ b/abi/0.2.json @@ -0,0 +1,313 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "AnyType": { + "anyOf": [ + { + "$ref": "#/definitions/ScalarType" + }, + { + "$ref": "#/definitions/ArrayType" + }, + { + "$ref": "#/definitions/MapType" + }, + { + "$ref": "#/definitions/RefType" + }, + { + "$ref": "#/definitions/ImportRefType" + } + ] + }, + "ArgumentDef": { + "properties": { + "kind": { + "enum": [ + "Argument" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "type": { + "$ref": "#/definitions/AnyType" + } + }, + "type": "object" + }, + "ArrayType": { + "properties": { + "item": { + "$ref": "#/definitions/OptionalType" + }, + "kind": { + "enum": [ + "Array" + ], + "type": "string" + } + }, + "type": "object" + }, + "EnumDef": { + "properties": { + "constants": { + "items": { + "type": "string" + }, + "type": "array" + }, + "kind": { + "enum": [ + "Enum" + ], + "type": "string" + }, + "name": { + "type": "string" + } + }, + "type": "object" + }, + "FunctionDef": { + "properties": { + "args": { + "items": { + "$ref": "#/definitions/ArgumentDef" + }, + "type": "array" + }, + "kind": { + "enum": [ + "Function" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "result": { + "$ref": "#/definitions/ResultDef" + } + }, + "type": "object" + }, + "ImportRefType": { + "properties": { + "import_id": { + "type": "string" + }, + "kind": { + "enum": [ + "ImportRef" + ], + "type": "string" + }, + "ref_kind": { + "$ref": "#/definitions/UniqueDefKind" + }, + "ref_name": { + "type": "string" + } + }, + "type": "object" + }, + "MapType": { + "properties": { + "key": { + "$ref": "#/definitions/MapKeyScalarType" + }, + "kind": { + "enum": [ + "Map" + ], + "type": "string" + }, + "value": { + "$ref": "#/definitions/OptionalType" + } + }, + "type": "object" + }, + "ObjectDef": { + "properties": { + "kind": { + "enum": [ + "Object" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "props": { + "items": { + "$ref": "#/definitions/PropertyDef" + }, + "type": "array" + } + }, + "type": "object" + }, + "OptionalType": { + "properties": { + "required": { + "type": "boolean" + }, + "type": { + "$ref": "#/definitions/AnyType" + } + }, + "type": "object" + }, + "PropertyDef": { + "properties": { + "kind": { + "enum": [ + "Property" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "type": { + "$ref": "#/definitions/AnyType" + } + }, + "type": "object" + }, + "RefType": { + "properties": { + "kind": { + "enum": [ + "Ref" + ], + "type": "string" + }, + "ref_kind": { + "$ref": "#/definitions/UniqueDefKind" + }, + "ref_name": { + "type": "string" + } + }, + "type": "object" + }, + "ResultDef": { + "properties": { + "kind": { + "enum": [ + "Result" + ], + "type": "string" + }, + "required": { + "type": "boolean" + }, + "type": { + "$ref": "#/definitions/AnyType" + } + }, + "type": "object" + }, + "ScalarTypeEnum": { + "enum": [ + "BigInt", + "BigNumber", + "Boolean", + "Bytes", + "Int", + "Int16", + "Int32", + "Int8", + "JSON", + "String", + "UInt", + "UInt16", + "UInt32", + "UInt8" + ], + "type": "string" + }, + "ScalarTypeMapKeyEnum": { + "enum": [ + "Int", + "Int16", + "Int32", + "Int8", + "String", + "UInt", + "UInt16", + "UInt32", + "UInt8" + ], + "type": "string" + }, + "MapKeyScalarType": { + "properties": { + "kind": { + "enum": [ + "Scalar" + ], + "type": "string" + }, + "scalar": { + "$ref": "#/definitions/ScalarTypeMapKeyEnum" + } + }, + "type": "object" + }, + "ScalarType": { + "properties": { + "kind": { + "enum": [ + "Scalar" + ], + "type": "string" + }, + "scalar": { + "$ref": "#/definitions/ScalarTypeEnum" + } + }, + "type": "object" + }, + "UniqueDefKind": { + "enum": [ + "Enum", + "Function", + "Object" + ], + "type": "string" + } + }, + "properties": { + "enums": { + "items": { + "$ref": "#/definitions/EnumDef" + }, + "type": "array" + }, + "functions": { + "items": { + "$ref": "#/definitions/FunctionDef" + }, + "type": "array" + }, + "objects": { + "items": { + "$ref": "#/definitions/ObjectDef" + }, + "type": "array" + } + }, + "type": "object" +} \ No newline at end of file diff --git a/abi/README.md b/abi/README.md new file mode 100644 index 0000000..1ab75f4 --- /dev/null +++ b/abi/README.md @@ -0,0 +1,29 @@ +# WRAP ABI (v0.2) +## Goals +- **Optimized:** The size and performance of the ABI should be optimized. +- **Functional:** The composition & usage of this ABI should be highly functional. It should not require expensive parsing / transformation. +- **Stable:** The ABI should be resistent to potential future changes, helping it remain stable and easily upgradeable. + +## Improvements Upon 0.1 +- **Smaller:** 0.2 is much smaller than 0.1, as we have removed a significant amount of redundant information from the ABI. +- **Easier To Parse:** 0.2's structured layout is easier to parse than 0.1, as well as it includes a few properties that help solve ambiguity, which required reparsing to solve. +- **Clearer Semantics:** In 0.2 we're proposing clearer semantic naming conventions, to help bring more structure to the ABI's ontology. + +## Decisions Made +1. **ABI Sub-Types:** The different sub-types contained within an ABI have been better defined: + * **"Definition"** = user-defined type (`MyObject`, `MyEnum`, etc). + * **"Type"** = built-in type (`UInt#`, `Int#`, `String`, etc). + * **"Reference"** = reference to a "Definition" +2. **Functions Not Methods:** In 0.1 we called the static functions exported by the module "methods", but since we have plans to introduce stateful objects w/ callable methods, we should rename this in 0.2. So, in 0.2 we will call them "module functions", and later will introduce "object methods". +3. **Imports Are Namespaced ABIs:** Instead of creating multiple properties on the `Abi` interface for each import type (ex: `importedObjects: ObjectDefinition[]`), we instead treat all imports as namespaced ABIs. This allows us to add a single property to the root ABI which contains all imports, `imports: ImportedAbi[]`, where `ImportedAbi` is a derived interface from `Abi` which adds `namespace` and `uri` properties. +4. **Heterogeneous < Homogenous Collections:** Currently the `Abi` interface stores each definition kind within a homogenous array (ex: `objects: ObjectDef[]`). It has been discussed that it may be better to make the `Abi` a heterogeneous collection of all possible definitions (ex: `type Abi = AnyDef[]`). This is also know as a "polymorphic array". After some consideration of 0.2's primary goals, we have decided that we prefer the homogenous collections approach because it is easier to parse, because you know ahead of time what the type of each element in the array is. +5. **Remove Comments:** In order to help optimize the ABI, we've removed all `comment` properties. Comments can instead be found on the original source file written by the user that was used to produce the ABI. +6. **Keyword `implements` no longer implies inheritance**: Currently, users use `implements` as `extends`. If they implement an interface, the interface's properties get automatically copied over to the implementation type. While this allows users to type less, it incorrectly behaves as inheritance; so we're removing that behavior. Now if `A implements B`, we will just validate that `A` contains the properties of `B`. +7. **Remove Interfaces and Capabilities:** All wrappers have implicit interface definitions (by having a schema) but only some have implementations. Therefore, there's no reason to restrict implementation wrappers types to be used as interfaces (with the `implements` keyword); or to only enable `getImplementations` capabilities in interface wrappers. So we're removing the concept of `interfaces` in favor of just using types, and enabling `getImplementation` capabilities for all modules. +8. **Import an imported ABI's imports**: Currently, it isn't possible to use a type that was imported in an import without manually re-exporting it, as referenced here: https://github.com/polywrap/toolchain/issues/1448. This will now be possible by importing it in its namespaced form. See the [example](abi/examples/imports/user.ts) +9. **Introduced `ImportRef` (references to an import)**: references to an imported definition are now defined as `ImportRef`s. All imported ABIs contain an `id` propertyand imported ABIs can also have nested imported ABIs inside of them. Therefore, types defined in nested imports can be referenced through a "route" of their IDs. For example, route `1.2.3` would point to an imported ABI with ID `3`, nested inside an imported ABI with ID `2`, nested inside an imported ABI with ID `1`. `ImportRef`s contain an `import_id` property that point to the referenced types's import route. + +## Future Plans + +- Research merkle-root hashes for root-definitions (function, object, enum, etc?) to help with validation execution time. +- Introduce the concept of "Runtime ABI", which would be a compressed hyper optimized ABI for small storage & download, and fast runtime traversal & introspection (hashes, etc). This ABI would be separate from the ABI that the CLI uses to generate code. This differentiation would help to avoid constraining ourselves too much with optimizations, in the ABI that we manipulate in the CLI and focus on "ease of work"; while also not holding back on optimizations for the "Runtime ABI" because it'd be harder to work with diff --git a/abi/abi-0.2.ts b/abi/abi-0.2.ts new file mode 100644 index 0000000..a8570cd --- /dev/null +++ b/abi/abi-0.2.ts @@ -0,0 +1,171 @@ +/// ABIs + +export interface AbiDefs { + functions?: FunctionDef[]; + objects?: ObjectDef[]; + enums?: EnumDef[]; +} + +export interface Abi extends AbiDefs { + version: "0.2"; + imports?: ImportedAbi[]; +} + +export type ImportAbiType = + | "wasm" + | "interface"; + +export interface ImportedAbi extends AbiDefs { + id: string; + uri: string; + type: ImportAbiType; + namespace: string; + imports?: ImportedAbi[]; +} + +/// Definitions (user-defined) + +export type UniqueDefKind = + | "Function" + | "Object" + | "Enum" + +export type DefKind = + | UniqueDefKind + | "Argument" + | "Result" + | "Property"; + +export interface Def { + kind: DefKind; +} + +export interface NamedDef extends Def { + name: string; +} + +export interface InlinedTypeDef extends Def, OptionalType { } + +export interface NamedTypeDef extends NamedDef, InlinedTypeDef { } + +export interface FunctionDef extends NamedDef { + kind: "Function"; + args: ArgumentDef[]; + result: ResultDef; +} + +export interface ArgumentDef extends NamedTypeDef { + kind: "Argument"; +} + +export interface ResultDef extends InlinedTypeDef { + kind: "Result"; +} + +export interface ObjectDef extends NamedDef { + kind: "Object"; + props: PropertyDef[]; +} + +export interface PropertyDef extends NamedTypeDef { + kind: "Property"; +} + +export interface EnumDef extends NamedDef { + kind: "Enum"; + constants: string[]; +} + +/// Types (built-ins) + +export type AnyType = + | ScalarType + | ArrayType + | MapType + | RefType + | ImportRefType; + +export type TypeKind = + | "Scalar" + | "Array" + | "Map" + | "Ref" + | "ImportRef"; + +export interface Type { + kind: TypeKind; +} + +export interface ScalarType< + TScalarTypeName extends ScalarTypeName = ScalarTypeName +> extends Type { + kind: "Scalar"; + scalar: TScalarTypeName; +} + +export interface ArrayType extends Type { + kind: "Array"; + item: OptionalType; +} + +export interface MapType extends Type { + kind: "Map"; + key: ScalarType; + value: OptionalType; +} + +export interface RefType extends Type { + kind: "Ref"; + ref_kind: UniqueDefKind; + ref_name: string; +} + +export interface ImportRefType extends Type { + kind: "ImportRef"; + import_id: string; + ref_kind: UniqueDefKind; + ref_name: string; +} + +export interface OptionalType { + required: boolean; + type: AnyType; +} + +/// Constants + +export const scalarTypeSet = { + UInt: "UInt", + UInt8: "UInt8", + UInt16: "UInt16", + UInt32: "UInt32", + Int: "Int", + Int8: "Int8", + Int16: "Int16", + Int32: "Int32", + String: "String", + Boolean: "Boolean", + Bytes: "Bytes", + // TODO: remove complex types + BigInt: "BigInt", + BigNumber: "BigNumber", + JSON: "JSON", +}; +export type ScalarTypeSet = typeof scalarTypeSet; + +export type ScalarTypeName = keyof ScalarTypeSet; + +export const mapKeyTypeSet = { + UInt: "UInt", + UInt8: "UInt8", + UInt16: "UInt16", + UInt32: "UInt32", + Int: "Int", + Int8: "Int8", + Int16: "Int16", + Int32: "Int32", + String: "String", +}; +export type MapKeyTypeSet = typeof mapKeyTypeSet; + +export type MapKeyTypeName = keyof MapKeyTypeSet; diff --git a/abi/examples/array.graphql b/abi/examples/array.graphql new file mode 100644 index 0000000..463cf72 --- /dev/null +++ b/abi/examples/array.graphql @@ -0,0 +1,7 @@ +type CustomType { + uArray: [UInt!]! + uOptArray: [UInt!] + uOptArrayOptArray: [[UInt32]]! + uArrayOptArrayArray: [[[UInt32!]!]]! + crazyArray: [[[[UInt32!]]!]] +} diff --git a/abi/examples/array.ts b/abi/examples/array.ts new file mode 100644 index 0000000..2f70cf5 --- /dev/null +++ b/abi/examples/array.ts @@ -0,0 +1,139 @@ +import { Abi } from "../abi-0.2"; + +const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "CustomType", + props: [ + { + kind: "Property", + required: true, + name: "uArray", + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "UInt" + } + } + } + }, + { + kind: "Property", + required: false, + name: "uOptArray", + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "UInt" + } + } + } + }, + { + kind: "Property", + required: true, + name: "uArray", + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "UInt" + } + } + } + }, + { + kind: "Property", + required: true, + name: "uOptArrayOptArray", + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Scalar", + scalar: "UInt32" + } + } + } + } + } + }, + { + kind: "Property", + required: true, + name: "uArrayOptArrayArray", + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "UInt32" + } + } + } + } + } + } + } + }, + { + kind: "Property", + required: false, + name: "crazyArray", + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Array", + item: { + required: false, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "UInt32" + } + } + } + } + } + } + } + } + } + } + ] + } + ] +}; diff --git a/abi/examples/enum.graphql b/abi/examples/enum.graphql new file mode 100644 index 0000000..dd0f9ca --- /dev/null +++ b/abi/examples/enum.graphql @@ -0,0 +1,8 @@ +enum Foo { + BAR, + BAZ +} + +type SomeObject { + prop: Foo! +} \ No newline at end of file diff --git a/abi/examples/enum.ts b/abi/examples/enum.ts new file mode 100644 index 0000000..3947d92 --- /dev/null +++ b/abi/examples/enum.ts @@ -0,0 +1,30 @@ +import { Abi } from "../abi-0.2"; + +const abi: Abi = { + version: "0.2", + enums: [ + { + kind: "Enum", + name: "Foo", + constants: ["BAR", "BAZ"] + } + ], + objects: [ + { + kind: "Object", + name: "SomeObject", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Ref", + ref_kind: "Enum", + ref_name: "Foo" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/abi/examples/function.graphql b/abi/examples/function.graphql new file mode 100644 index 0000000..81505e5 --- /dev/null +++ b/abi/examples/function.graphql @@ -0,0 +1,5 @@ +type Module { + fooFunc( + arg: String! + ): String! +} diff --git a/abi/examples/function.ts b/abi/examples/function.ts new file mode 100644 index 0000000..e5df6ef --- /dev/null +++ b/abi/examples/function.ts @@ -0,0 +1,30 @@ +import { Abi } from "../abi-0.2"; + +const abi: Abi = { + version: "0.2", + functions: [ + { + kind: "Function", + name: "fooFunc", + args: [ + { + kind: "Argument", + name: "arg", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true, + } + ], + result: { + kind: "Result", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true + } + } + ] +}; diff --git a/abi/examples/implements.graphql b/abi/examples/implements.graphql new file mode 100644 index 0000000..c610e90 --- /dev/null +++ b/abi/examples/implements.graphql @@ -0,0 +1,17 @@ +#import { Module, Foo } into External from "wrap://ens/external.eth" + +type Module implements External_Module { + func(arg: Int!): Int! + exec(arg: String!): String! +} + +type Bar implements External_Foo { + propA: Int! + propB: String! +} + +type Baz implements Bar { + propA: Int! + propB: String! + propC: String! +} diff --git a/abi/examples/implements.ts b/abi/examples/implements.ts new file mode 100644 index 0000000..6728551 --- /dev/null +++ b/abi/examples/implements.ts @@ -0,0 +1,162 @@ +import { Abi } from "../abi-0.2"; + +const abi: Abi = { + version: "0.2", + imports: [ + { + namespace: "External", + uri: "wrap://ens/external.eth", + type: "wasm", + id: "1", + functions: [ + { + kind: "Function", + name: "func", + args: [ + { + kind: "Argument", + name: "arg", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + } + ], + result: { + kind: "Result", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + } + } + ], + objects: [ + { + kind: "Object", + name: "Foo", + props: [ + { + kind: "Property", + name: "propA", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + } + ] + } + ] + } + ], + functions: [ + { + kind: "Function", + name: "func", + args: [ + { + kind: "Argument", + name: "arg", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + } + ], + result: { + kind: "Result", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + } + }, + { + kind: "Function", + name: "exec", + args: [ + { + kind: "Argument", + name: "arg", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ], + result: { + kind: "Result", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + } + ], + objects: [ + { + kind: "Object", + name: "Bar", + props: [ + { + kind: "Property", + name: "propA", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + }, + { + kind: "Property", + name: "propB", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + }, + { + kind: "Object", + name: "Baz", + props: [ + { + kind: "Property", + name: "propA", + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + }, + { + kind: "Property", + name: "propB", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + }, + { + kind: "Property", + name: "propC", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/abi/examples/imports/bar.graphql b/abi/examples/imports/bar.graphql new file mode 100644 index 0000000..fbbbdd6 --- /dev/null +++ b/abi/examples/imports/bar.graphql @@ -0,0 +1,10 @@ +#import { DataType, SecondType } into Lib1 from "lib1" + +type Baz { + prop: String! +} + +type Obj1 { + data: Lib1_DataType! + data2: Baz! +} \ No newline at end of file diff --git a/abi/examples/imports/bar.ts b/abi/examples/imports/bar.ts new file mode 100644 index 0000000..f484939 --- /dev/null +++ b/abi/examples/imports/bar.ts @@ -0,0 +1,77 @@ +import { Abi } from "../../abi-0.2"; + +const abi: Abi = { + version: "0.2", + imports: [ + { + uri: "lib1", + type: "wasm", + id: "0", + namespace: "Lib1", + objects: [{ + kind: "Object", + name: "DataType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "UInt32" + } + } + ] + }, + { + kind: "Object", + name: "SecondType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + }] + } + ], + objects: [ + { + kind: "Object", + name: "Obj1", + props: [ + { + kind: "Property", + name: "data", + required: true, + type: { + kind: "ImportRef", + import_id: "0", + ref_kind: "Object", + ref_name: "DataType" + } + } + ] + }, + { + kind: "Object", + name: "Baz", + props: [ + { + kind: "Property", + name: "data2", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/abi/examples/imports/foo.graphql b/abi/examples/imports/foo.graphql new file mode 100644 index 0000000..8aae8b3 --- /dev/null +++ b/abi/examples/imports/foo.graphql @@ -0,0 +1,5 @@ +#import { DataType } into Lib2 from "lib2" + +type Obj2 { + data: Lib2_DataType! +} \ No newline at end of file diff --git a/abi/examples/imports/foo.ts b/abi/examples/imports/foo.ts new file mode 100644 index 0000000..b728dce --- /dev/null +++ b/abi/examples/imports/foo.ts @@ -0,0 +1,47 @@ +import { Abi } from "../../abi-0.2"; + +const abi: Abi = { + version: "0.2", + imports: [ + { + uri: "lib2", + type: "wasm", + id: "0", + namespace: "Lib2", + objects: [{ + kind: "Object", + name: "DataType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "Bytes" + } + } + ] + }] + } + ], + objects: [ + { + kind: "Object", + name: "Obj2", + props: [ + { + kind: "Property", + name: "data", + required: true, + type: { + kind: "ImportRef", + import_id: "0", + ref_kind: "Object", + ref_name: "DataType" + } + } + ] + }, + ] +} \ No newline at end of file diff --git a/abi/examples/imports/lib1.graphql b/abi/examples/imports/lib1.graphql new file mode 100644 index 0000000..0d36b91 --- /dev/null +++ b/abi/examples/imports/lib1.graphql @@ -0,0 +1,7 @@ +type DataType { + prop: UInt32! +} + +type SecondType { + prop: String! +} \ No newline at end of file diff --git a/abi/examples/imports/lib1.ts b/abi/examples/imports/lib1.ts new file mode 100644 index 0000000..a92345b --- /dev/null +++ b/abi/examples/imports/lib1.ts @@ -0,0 +1,37 @@ +import { Abi } from "../../abi-0.2"; + +const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "DataType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "UInt32" + } + } + ] + }, + { + kind: "Object", + name: "SecondType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + }, + ] +} \ No newline at end of file diff --git a/abi/examples/imports/lib2.graphql b/abi/examples/imports/lib2.graphql new file mode 100644 index 0000000..1b6863f --- /dev/null +++ b/abi/examples/imports/lib2.graphql @@ -0,0 +1,3 @@ +type DataType { + prop: Bytes! +} \ No newline at end of file diff --git a/abi/examples/imports/lib2.ts b/abi/examples/imports/lib2.ts new file mode 100644 index 0000000..5ac338a --- /dev/null +++ b/abi/examples/imports/lib2.ts @@ -0,0 +1,22 @@ +import { Abi } from "../../abi-0.2"; + +const abi: Abi = { + version: "0.2", + objects: [ + { + kind: "Object", + name: "DataType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "Bytes" + } + } + ] + }, + ] +} \ No newline at end of file diff --git a/abi/examples/imports/user.graphql b/abi/examples/imports/user.graphql new file mode 100644 index 0000000..729c6bc --- /dev/null +++ b/abi/examples/imports/user.graphql @@ -0,0 +1,7 @@ +#import { Obj1, Lib1_SecondType } into Bar from "bar" +#import { Obj2 } into Foo from "foo" + +type UserObj { + obj1: Bar_Obj1! + obj2: Foo_Obj2! +} \ No newline at end of file diff --git a/abi/examples/imports/user.ts b/abi/examples/imports/user.ts new file mode 100644 index 0000000..c3ed551 --- /dev/null +++ b/abi/examples/imports/user.ts @@ -0,0 +1,160 @@ +import { Abi } from "../../abi-0.2"; + +const abi: Abi = { + version: "0.2", + imports: [ + { + id: "0", + namespace: "Bar", + uri: "bar", + type: "wasm", + imports: [ + { + uri: "lib1", + type: "wasm", + id: "0", + namespace: "Lib1", + objects: [{ + kind: "Object", + name: "DataType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "UInt32" + } + } + ] + }, + { + kind: "Object", + name: "SecondType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + }] + }, + ], + objects: [ + { + kind: "Object", + name: "Baz", + props: [ + { + kind: "Property", + name: "data2", + required: true, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + }, + { + kind: "Object", + name: "Obj1", + props: [ + { + kind: "Property", + name: "data", + required: true, + type: { + kind: "ImportRef", + import_id: "0", + ref_kind: "Object", + ref_name: "DataType" + } + } + ] + }] + }, + { + id: "1", + namespace: "Foo", + uri: "foo", + type: "wasm", + imports: [ + { + uri: "lib2", + type: "wasm", + id: "0", + namespace: "Lib2", + objects: [{ + kind: "Object", + name: "DataType", + props: [ + { + kind: "Property", + name: "prop", + required: true, + type: { + kind: "Scalar", + scalar: "Bytes" + } + } + ] + }] + } + ], + objects: [{ + kind: "Object", + name: "Obj2", + props: [ + { + kind: "Property", + name: "data", + required: true, + type: { + kind: "ImportRef", + import_id: "0", + ref_kind: "Object", + ref_name: "DataType" + } + } + ] + }] + }, + ], + objects: [ + { + kind: "Object", + name: "UserObj", + props: [ + { + kind: "Property", + name: "obj1", + required: true, + type: { + import_id: "0", + kind: "ImportRef", + ref_kind: "Object", + ref_name: "Obj1" + } + }, + { + kind: "Property", + name: "obj2", + required: true, + type: { + import_id: "0.0", + kind: "ImportRef", + ref_kind: "Object", + ref_name: "Obj2" + } + } + ] + }, + ] +} \ No newline at end of file diff --git a/abi/examples/map.graphql b/abi/examples/map.graphql new file mode 100644 index 0000000..a868f00 --- /dev/null +++ b/abi/examples/map.graphql @@ -0,0 +1,17 @@ +type Module implements Interface_Module { + mapMethod( + map: Map @annotate(type: "Map") + ): Map @annotate(type: "Map") +} + +type CustomType { + map: Map! @annotate(type: "Map!") + mapOfArr: Map! @annotate(type: "Map!") + mapOfObj: Map! @annotate(type: "Map!") + mapOfArrOfObj: Map! @annotate(type: "Map!") + mapOfMapOfObj: Map! @annotate(type: "Map!>!") +} + +type AnotherType { + prop: String +} \ No newline at end of file diff --git a/abi/examples/map.ts b/abi/examples/map.ts new file mode 100644 index 0000000..d0f921f --- /dev/null +++ b/abi/examples/map.ts @@ -0,0 +1,195 @@ +import { Abi } from "../abi-0.2"; + +// Map values are always required + +const abi: Abi = { + version: "0.2", + functions: [ + { + kind: "Function", + name: "mapMethod", + args: [ + { + kind: "Argument", + name: "map", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Scalar", + scalar: "Int", + } + } + }, + required: false, + } + ], + result: { + kind: "Result", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Scalar", + scalar: "Int", + } + } + }, + required: false, + } + } + ], + objects: [ + { + kind: "Object", + name: "CustomType", + props: [ + { + kind: "Property", + required: true, + name: "map", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String", + }, + value: { + required: false, + type: { + kind: "Scalar", + scalar: "Int", + } + } + } + }, + { + kind: "Property", + required: true, + name: "mapOfArr", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String", + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Scalar", + scalar: "Int" + } + }, + } + } + } + }, + { + kind: "Property", + required: true, + name: "mapOfObj", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String", + }, + value: { + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "AnotherType" + } + } + } + }, + { + kind: "Property", + required: true, + name: "mapOfArrOfObj", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String", + }, + value: { + required: true, + type: { + kind: "Array", + item: { + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "AnotherType" + } + } + } + } + } + }, + { + kind: "Property", + required: true, + name: "mapOfMapOfObj", + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String", + }, + value: { + required: true, + type: { + kind: "Map", + key: { + kind: "Scalar", + scalar: "String" + }, + value: { + required: true, + type: { + kind: "Ref", + ref_kind: "Object", + ref_name: "AnotherType" + } + } + } + } + } + } + ] + }, + { + kind: "Object", + name: "AnotherType", + props: [ + { + kind: "Property", + name: "prop", + required: false, + type: { + kind: "Scalar", + scalar: "String" + } + } + ] + } + ] +}; diff --git a/abi/examples/sanity.graphql b/abi/examples/sanity.graphql new file mode 100644 index 0000000..1bb01d1 --- /dev/null +++ b/abi/examples/sanity.graphql @@ -0,0 +1,18 @@ +type Module { + func1( + arg: Bytes! + ): UInt32! + + func2( + custom: Custom! + ): Int32 +} + +type Custom { + prop1: String! + nested: Nested +} + +type Nested { + prop: UInt8 +} diff --git a/abi/examples/sanity.ts b/abi/examples/sanity.ts new file mode 100644 index 0000000..3c5d3c5 --- /dev/null +++ b/abi/examples/sanity.ts @@ -0,0 +1,96 @@ +import { Abi } from "../abi-0.2"; + +const abi: Abi = { + version: "0.2", + functions: [ + { + kind: "Function", + name: "func1", + args: [ + { + kind: "Argument", + name: "arg", + type: { + kind: "Scalar", + scalar: "Bytes" + }, + required: true + } + ], + result: { + kind: "Result", + type: { + kind: "Scalar", + scalar: "UInt32", + }, + required: true + } + }, + { + kind: "Function", + name: "func2", + args: [ + { + kind: "Argument", + name: "custom", + type: { + kind: "Ref", + ref_name: "Custom", + ref_kind: "Object" + }, + required: true + } + ], + result: { + kind: "Result", + type: { + kind: "Scalar", + scalar: "Int32" + }, + required: false + } + } + ], + objects: [ + { + kind: "Object", + name: "Custom", + props: [ + { + kind: "Property", + name: "prop1", + type: { + kind: "Scalar", + scalar: "String" + }, + required: true + }, + { + kind: "Property", + name: "nested", + type: { + kind: "Ref", + ref_name: "Nested", + ref_kind: "Object" + }, + required: false + } + ] + }, + { + kind: "Object", + name: "Nested", + props: [ + { + kind: "Property", + name: "prop", + type: { + kind: "Scalar", + scalar: "UInt8" + }, + required: false + }, + ] + } + ] +}; diff --git a/abi/examples/todo b/abi/examples/todo new file mode 100644 index 0000000..0cfca68 --- /dev/null +++ b/abi/examples/todo @@ -0,0 +1,23 @@ +TODO: +x add wrapper type to imports +x parity with old ABI +x use maps for unique def kinds +- use enums instead of strings +x JSON-schema +- Transpile to TS +x notes on things removed from old ABI (capabilities) +x compile the ts example files to make sure they implement the ABI well +x document the choices around imports & how import "routing" works for importing the imports of your import. (ex: "1.2.3") +- migration script from 0.1 to 0.2? + +Test-Cases: +x referencing imports +x implemented interfaces + +After Init PR: +- fix complex types + +Future: +x "runtime abi" is a compressed hyper optimized ABI for small storage & download, and fast runtime traversal & introspection (hashes, etc) +x research merkle-root hashes for root-definitions (function, object, enum, etc?) to help with validation execution time + - "does interface A implement interface B?"