@ensdomains/ensjs 3.0.0-alpha.6 → 3.0.0-alpha.7
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/cjs/GqlManager.d.ts +3 -0
- package/dist/cjs/GqlManager.js +62 -2
- package/dist/cjs/utils/normalise.d.ts +1 -1
- package/dist/cjs/utils/normalise.js +10 -3
- package/dist/esm/GqlManager.d.ts +3 -0
- package/dist/esm/GqlManager.js +58 -2
- package/dist/esm/utils/normalise.d.ts +1 -1
- package/dist/esm/utils/normalise.js +10 -3
- package/package.json +6 -4
- package/src/GqlManager.test.ts +170 -0
- package/src/GqlManager.ts +67 -2
- package/src/utils/normalise.ts +10 -4
package/dist/cjs/GqlManager.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare const enter: (node: any) => any;
|
|
2
|
+
export declare const requestMiddleware: (visit: any, parse: any) => (request: any) => any;
|
|
3
|
+
export declare const responseMiddleware: (traverse: any) => (response: any) => any;
|
|
1
4
|
export default class GqlManager {
|
|
2
5
|
gql: any;
|
|
3
6
|
client?: any | null;
|
package/dist/cjs/GqlManager.js
CHANGED
|
@@ -23,13 +23,73 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.responseMiddleware = exports.requestMiddleware = exports.enter = void 0;
|
|
27
|
+
// @ts-nocheck
|
|
28
|
+
const normalise_1 = require("./utils/normalise");
|
|
29
|
+
const generateSelection = (selection) => ({
|
|
30
|
+
kind: 'Field',
|
|
31
|
+
name: {
|
|
32
|
+
kind: 'Name',
|
|
33
|
+
value: selection,
|
|
34
|
+
},
|
|
35
|
+
arguments: [],
|
|
36
|
+
directives: [],
|
|
37
|
+
alias: undefined,
|
|
38
|
+
selectionSet: undefined,
|
|
39
|
+
});
|
|
40
|
+
const enter = (node) => {
|
|
41
|
+
if (node.kind === 'SelectionSet') {
|
|
42
|
+
const id = node.selections.find((x) => x.name && x.name.value === 'id');
|
|
43
|
+
const name = node.selections.find((x) => x.name && x.name.value === 'name');
|
|
44
|
+
if (!id && name) {
|
|
45
|
+
node.selections = [...node.selections, generateSelection('id')];
|
|
46
|
+
return node;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.enter = enter;
|
|
51
|
+
const requestMiddleware = (visit, parse) => (request) => {
|
|
52
|
+
const requestBody = JSON.parse(request.body);
|
|
53
|
+
const rawQuery = requestBody.query;
|
|
54
|
+
const parsedQuery = parse(rawQuery);
|
|
55
|
+
const updatedQuery = visit(parsedQuery, { enter: exports.enter });
|
|
56
|
+
const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body };
|
|
57
|
+
return {
|
|
58
|
+
...request,
|
|
59
|
+
body: JSON.stringify(updatedBody),
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
exports.requestMiddleware = requestMiddleware;
|
|
63
|
+
const responseMiddleware = (traverse) => (response) => {
|
|
64
|
+
traverse(response).forEach(function (responseItem) {
|
|
65
|
+
if (responseItem instanceof Object && responseItem.name) {
|
|
66
|
+
//Name already in hashed form
|
|
67
|
+
if (responseItem.name && responseItem.name.includes('[')) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const hashedName = (0, normalise_1.namehash)(responseItem.name);
|
|
71
|
+
if (responseItem.id !== hashedName) {
|
|
72
|
+
this.update({ ...responseItem, name: hashedName, invalidName: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return response;
|
|
77
|
+
};
|
|
78
|
+
exports.responseMiddleware = responseMiddleware;
|
|
26
79
|
class GqlManager {
|
|
27
80
|
gql = () => null;
|
|
28
81
|
client = null;
|
|
29
82
|
setUrl = async (url) => {
|
|
30
83
|
if (url) {
|
|
31
|
-
const imported = await Promise.
|
|
32
|
-
|
|
84
|
+
const [imported, traverse, { visit, parse }] = await Promise.all([
|
|
85
|
+
Promise.resolve().then(() => __importStar(require('graphql-request'))),
|
|
86
|
+
Promise.resolve().then(() => __importStar(require('traverse'))),
|
|
87
|
+
Promise.resolve().then(() => __importStar(require('graphql/language'))),
|
|
88
|
+
]);
|
|
89
|
+
this.client = new imported.GraphQLClient(url, {
|
|
90
|
+
requestMiddleware: (0, exports.requestMiddleware)(visit, parse),
|
|
91
|
+
responseMiddleware: (0, exports.responseMiddleware)(traverse.default),
|
|
92
|
+
});
|
|
33
93
|
this.gql = imported.gql;
|
|
34
94
|
}
|
|
35
95
|
else {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const normalise: (name: string) => any;
|
|
2
|
-
export declare const namehash: (
|
|
2
|
+
export declare const namehash: (name: string) => string;
|
|
@@ -6,17 +6,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.namehash = exports.normalise = void 0;
|
|
7
7
|
const utils_1 = require("ethers/lib/utils");
|
|
8
8
|
const uts46bundle_js_1 = __importDefault(require("idna-uts46-hx/uts46bundle.js"));
|
|
9
|
+
const labels_1 = require("./labels");
|
|
9
10
|
const zeros = new Uint8Array(32);
|
|
10
11
|
zeros.fill(0);
|
|
11
12
|
const normalise = (name) => name ? uts46bundle_js_1.default.toUnicode(name, { useStd3ASCII: true }) : name;
|
|
12
13
|
exports.normalise = normalise;
|
|
13
|
-
const namehash = (
|
|
14
|
+
const namehash = (name) => {
|
|
14
15
|
let result = zeros;
|
|
15
|
-
const name = (0, exports.normalise)(inputName);
|
|
16
16
|
if (name) {
|
|
17
17
|
const labels = name.split('.');
|
|
18
18
|
for (var i = labels.length - 1; i >= 0; i--) {
|
|
19
|
-
|
|
19
|
+
let labelSha;
|
|
20
|
+
if ((0, labels_1.isEncodedLabelhash)(labels[i])) {
|
|
21
|
+
labelSha = (0, labels_1.decodeLabelhash)(labels[i]);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
const normalised = (0, exports.normalise)(labels[i]);
|
|
25
|
+
labelSha = (0, utils_1.keccak256)((0, utils_1.toUtf8Bytes)(normalised));
|
|
26
|
+
}
|
|
20
27
|
result = (0, utils_1.keccak256)((0, utils_1.concat)([result, labelSha]));
|
|
21
28
|
}
|
|
22
29
|
}
|
package/dist/esm/GqlManager.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare const enter: (node: any) => any;
|
|
2
|
+
export declare const requestMiddleware: (visit: any, parse: any) => (request: any) => any;
|
|
3
|
+
export declare const responseMiddleware: (traverse: any) => (response: any) => any;
|
|
1
4
|
export default class GqlManager {
|
|
2
5
|
gql: any;
|
|
3
6
|
client?: any | null;
|
package/dist/esm/GqlManager.js
CHANGED
|
@@ -1,11 +1,67 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { namehash } from './utils/normalise';
|
|
3
|
+
const generateSelection = (selection) => ({
|
|
4
|
+
kind: 'Field',
|
|
5
|
+
name: {
|
|
6
|
+
kind: 'Name',
|
|
7
|
+
value: selection,
|
|
8
|
+
},
|
|
9
|
+
arguments: [],
|
|
10
|
+
directives: [],
|
|
11
|
+
alias: undefined,
|
|
12
|
+
selectionSet: undefined,
|
|
13
|
+
});
|
|
14
|
+
export const enter = (node) => {
|
|
15
|
+
if (node.kind === 'SelectionSet') {
|
|
16
|
+
const id = node.selections.find((x) => x.name && x.name.value === 'id');
|
|
17
|
+
const name = node.selections.find((x) => x.name && x.name.value === 'name');
|
|
18
|
+
if (!id && name) {
|
|
19
|
+
node.selections = [...node.selections, generateSelection('id')];
|
|
20
|
+
return node;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export const requestMiddleware = (visit, parse) => (request) => {
|
|
25
|
+
const requestBody = JSON.parse(request.body);
|
|
26
|
+
const rawQuery = requestBody.query;
|
|
27
|
+
const parsedQuery = parse(rawQuery);
|
|
28
|
+
const updatedQuery = visit(parsedQuery, { enter });
|
|
29
|
+
const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body };
|
|
30
|
+
return {
|
|
31
|
+
...request,
|
|
32
|
+
body: JSON.stringify(updatedBody),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export const responseMiddleware = (traverse) => (response) => {
|
|
36
|
+
traverse(response).forEach(function (responseItem) {
|
|
37
|
+
if (responseItem instanceof Object && responseItem.name) {
|
|
38
|
+
//Name already in hashed form
|
|
39
|
+
if (responseItem.name && responseItem.name.includes('[')) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const hashedName = namehash(responseItem.name);
|
|
43
|
+
if (responseItem.id !== hashedName) {
|
|
44
|
+
this.update({ ...responseItem, name: hashedName, invalidName: true });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return response;
|
|
49
|
+
};
|
|
1
50
|
export default class GqlManager {
|
|
2
51
|
constructor() {
|
|
3
52
|
this.gql = () => null;
|
|
4
53
|
this.client = null;
|
|
5
54
|
this.setUrl = async (url) => {
|
|
6
55
|
if (url) {
|
|
7
|
-
const imported = await
|
|
8
|
-
|
|
56
|
+
const [imported, traverse, { visit, parse }] = await Promise.all([
|
|
57
|
+
import('graphql-request'),
|
|
58
|
+
import('traverse'),
|
|
59
|
+
import('graphql/language'),
|
|
60
|
+
]);
|
|
61
|
+
this.client = new imported.GraphQLClient(url, {
|
|
62
|
+
requestMiddleware: requestMiddleware(visit, parse),
|
|
63
|
+
responseMiddleware: responseMiddleware(traverse.default),
|
|
64
|
+
});
|
|
9
65
|
this.gql = imported.gql;
|
|
10
66
|
}
|
|
11
67
|
else {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const normalise: (name: string) => any;
|
|
2
|
-
export declare const namehash: (
|
|
2
|
+
export declare const namehash: (name: string) => string;
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { concat, hexlify, keccak256, toUtf8Bytes } from 'ethers/lib/utils';
|
|
2
2
|
import uts46 from 'idna-uts46-hx/uts46bundle.js';
|
|
3
|
+
import { decodeLabelhash, isEncodedLabelhash } from './labels';
|
|
3
4
|
const zeros = new Uint8Array(32);
|
|
4
5
|
zeros.fill(0);
|
|
5
6
|
export const normalise = (name) => name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name;
|
|
6
|
-
export const namehash = (
|
|
7
|
+
export const namehash = (name) => {
|
|
7
8
|
let result = zeros;
|
|
8
|
-
const name = normalise(inputName);
|
|
9
9
|
if (name) {
|
|
10
10
|
const labels = name.split('.');
|
|
11
11
|
for (var i = labels.length - 1; i >= 0; i--) {
|
|
12
|
-
|
|
12
|
+
let labelSha;
|
|
13
|
+
if (isEncodedLabelhash(labels[i])) {
|
|
14
|
+
labelSha = decodeLabelhash(labels[i]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const normalised = normalise(labels[i]);
|
|
18
|
+
labelSha = keccak256(toUtf8Bytes(normalised));
|
|
19
|
+
}
|
|
13
20
|
result = keccak256(concat([result, labelSha]));
|
|
14
21
|
}
|
|
15
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ensdomains/ensjs",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.7",
|
|
4
4
|
"description": "ENS javascript library for contract interaction",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/cjs/index.d.ts",
|
|
@@ -46,8 +46,9 @@
|
|
|
46
46
|
"dns-packet": "^5.3.1",
|
|
47
47
|
"ethers": "^5.6.1",
|
|
48
48
|
"graphql": "^16.3.0",
|
|
49
|
-
"graphql-request": "
|
|
50
|
-
"idna-uts46-hx": "3.4.0"
|
|
49
|
+
"graphql-request": "next",
|
|
50
|
+
"idna-uts46-hx": "3.4.0",
|
|
51
|
+
"traverse": "^0.6.6"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@ensdomains/buffer": "^0.0.13",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"@typechain/ethers-v5": "^10.0.0",
|
|
62
63
|
"@types/bn.js": "^5.1.0",
|
|
63
64
|
"@types/jest": "^27.4.1",
|
|
65
|
+
"@types/traverse": "^0.6.32",
|
|
64
66
|
"dotenv": "^16.0.0",
|
|
65
67
|
"ens-contracts": "github:ensdomains/ens-contracts#head=master&commit=3ecc56b14beb4aae8296f8a94f7c0d095e62fd93",
|
|
66
68
|
"hardhat": "^2.9.3",
|
|
@@ -81,5 +83,5 @@
|
|
|
81
83
|
"peerDependencies": {
|
|
82
84
|
"ethers": "*"
|
|
83
85
|
},
|
|
84
|
-
"stableVersion": "3.0.0-alpha.
|
|
86
|
+
"stableVersion": "3.0.0-alpha.6"
|
|
85
87
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import traverse from 'traverse'
|
|
2
|
+
import { visit, parse } from 'graphql'
|
|
3
|
+
|
|
4
|
+
import { requestMiddleware, responseMiddleware, enter } from './GqlManager'
|
|
5
|
+
import { namehash } from './utils/normalise'
|
|
6
|
+
|
|
7
|
+
describe('GqlManager', () => {
|
|
8
|
+
const queryWithoutId = `
|
|
9
|
+
query getNames($id: ID!, $expiryDate: Int) {
|
|
10
|
+
account(id: $id) {
|
|
11
|
+
registrations(first: 1000, where: { expiryDate_gt: $expiryDate }) {
|
|
12
|
+
registrationDate
|
|
13
|
+
expiryDate
|
|
14
|
+
domain {
|
|
15
|
+
labelName
|
|
16
|
+
labelhash
|
|
17
|
+
name
|
|
18
|
+
isMigrated
|
|
19
|
+
parent {
|
|
20
|
+
name
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
domains(first: 1000) {
|
|
25
|
+
labelName
|
|
26
|
+
labelhash
|
|
27
|
+
name
|
|
28
|
+
isMigrated
|
|
29
|
+
parent {
|
|
30
|
+
name
|
|
31
|
+
}
|
|
32
|
+
createdAt
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
const mockRequest = {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ query: queryWithoutId }),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const mockResponse = {
|
|
47
|
+
data: {
|
|
48
|
+
account: {
|
|
49
|
+
registrations: [
|
|
50
|
+
{
|
|
51
|
+
registrationDate: '1659245504',
|
|
52
|
+
expiryDate: '1690781504',
|
|
53
|
+
domain: {
|
|
54
|
+
id: '0xb1ccf7f6bb648f39ba48bf80f4a8a4522f474ee7d1edd6ea65f7fa51fe8b1612',
|
|
55
|
+
labelName: 'randydandy',
|
|
56
|
+
labelhash:
|
|
57
|
+
'0x63fd5bfdd0b1eb9f4c33c209b7c3e7dcb87a761d926a65e267f7cb99ee313ddd',
|
|
58
|
+
name: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
59
|
+
isMigrated: true,
|
|
60
|
+
parent: {
|
|
61
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
62
|
+
invalidName: true,
|
|
63
|
+
},
|
|
64
|
+
invalidName: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
registrationDate: '1635284756',
|
|
69
|
+
expiryDate: '1666841708',
|
|
70
|
+
domain: {
|
|
71
|
+
id: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
72
|
+
labelName: 'randydandy',
|
|
73
|
+
labelhash:
|
|
74
|
+
'0x6a877a8d92ad83c8d044d0c5b69aa0da3050f4d653dcc149fca952d6439e4105',
|
|
75
|
+
name: 'randydandy.eth',
|
|
76
|
+
isMigrated: true,
|
|
77
|
+
parent: {
|
|
78
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
79
|
+
invalidName: true,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
registrationDate: '1659250465',
|
|
85
|
+
expiryDate: '1690786465',
|
|
86
|
+
domain: {
|
|
87
|
+
id: '0xaf119e72f050e5acad6b2d97826a7afb45afe9407376a2ab8240b62173b2d7c2',
|
|
88
|
+
labelName: 'nullbyte',
|
|
89
|
+
labelhash:
|
|
90
|
+
'0xc8985f8323b3dc707003a6ef5b379d9e8a058b6d6bcc2cfb307c61ca36920e27',
|
|
91
|
+
name: '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
|
|
92
|
+
isMigrated: true,
|
|
93
|
+
parent: {
|
|
94
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
95
|
+
invalidName: true,
|
|
96
|
+
},
|
|
97
|
+
invalidName: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
domains: [
|
|
102
|
+
{
|
|
103
|
+
id: '0xaf119e72f050e5acad6b2d97826a7afb45afe9407376a2ab8240b62173b2d7c2',
|
|
104
|
+
labelName: 'nullbyte',
|
|
105
|
+
labelhash:
|
|
106
|
+
'0xc8985f8323b3dc707003a6ef5b379d9e8a058b6d6bcc2cfb307c61ca36920e27',
|
|
107
|
+
name: '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
|
|
108
|
+
isMigrated: true,
|
|
109
|
+
parent: {
|
|
110
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
111
|
+
invalidName: true,
|
|
112
|
+
},
|
|
113
|
+
createdAt: '1659250465',
|
|
114
|
+
invalidName: true,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: '0xb1ccf7f6bb648f39ba48bf80f4a8a4522f474ee7d1edd6ea65f7fa51fe8b1612',
|
|
118
|
+
labelName: 'randydandy',
|
|
119
|
+
labelhash:
|
|
120
|
+
'0x63fd5bfdd0b1eb9f4c33c209b7c3e7dcb87a761d926a65e267f7cb99ee313ddd',
|
|
121
|
+
name: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
122
|
+
isMigrated: true,
|
|
123
|
+
parent: {
|
|
124
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
125
|
+
invalidName: true,
|
|
126
|
+
},
|
|
127
|
+
createdAt: '1659245504',
|
|
128
|
+
invalidName: true,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
132
|
+
labelName: 'randydandy',
|
|
133
|
+
labelhash:
|
|
134
|
+
'0x6a877a8d92ad83c8d044d0c5b69aa0da3050f4d653dcc149fca952d6439e4105',
|
|
135
|
+
name: 'randydandy.eth',
|
|
136
|
+
isMigrated: true,
|
|
137
|
+
parent: {
|
|
138
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
139
|
+
invalidName: true,
|
|
140
|
+
},
|
|
141
|
+
createdAt: '1635284756',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
headers: {
|
|
147
|
+
map: {
|
|
148
|
+
'content-type': 'application/json',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
status: 200,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
describe('requestMiddleware', () => {
|
|
155
|
+
it('should add id to a SelectionSet if name is present and id is not', () => {
|
|
156
|
+
const result = requestMiddleware(visit, parse)(mockRequest)
|
|
157
|
+
expect(result.body.includes('id')).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
describe('responseMiddleware', () => {
|
|
161
|
+
it('should replace name with the namehash when there is an invalid name and id combo', () => {
|
|
162
|
+
const result = responseMiddleware(traverse)(mockResponse)
|
|
163
|
+
expect(result.data.account.domains[0].name).toBe(
|
|
164
|
+
namehash(
|
|
165
|
+
'0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
})
|
package/src/GqlManager.ts
CHANGED
|
@@ -1,11 +1,76 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { namehash } from './utils/normalise'
|
|
3
|
+
|
|
4
|
+
const generateSelection = (selection: any) => ({
|
|
5
|
+
kind: 'Field',
|
|
6
|
+
name: {
|
|
7
|
+
kind: 'Name',
|
|
8
|
+
value: selection,
|
|
9
|
+
},
|
|
10
|
+
arguments: [],
|
|
11
|
+
directives: [],
|
|
12
|
+
alias: undefined,
|
|
13
|
+
selectionSet: undefined,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export const enter = (node: any) => {
|
|
17
|
+
if (node.kind === 'SelectionSet') {
|
|
18
|
+
const id = node.selections.find((x) => x.name && x.name.value === 'id')
|
|
19
|
+
const name = node.selections.find((x) => x.name && x.name.value === 'name')
|
|
20
|
+
|
|
21
|
+
if (!id && name) {
|
|
22
|
+
node.selections = [...node.selections, generateSelection('id')]
|
|
23
|
+
return node
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const requestMiddleware = (visit, parse) => (request: any) => {
|
|
29
|
+
const requestBody = JSON.parse(request.body)
|
|
30
|
+
const rawQuery = requestBody.query
|
|
31
|
+
const parsedQuery = parse(rawQuery)
|
|
32
|
+
const updatedQuery = visit(parsedQuery, { enter })
|
|
33
|
+
const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body }
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
...request,
|
|
37
|
+
body: JSON.stringify(updatedBody),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const responseMiddleware = (traverse) => (response: any) => {
|
|
42
|
+
traverse(response).forEach(function (responseItem: any) {
|
|
43
|
+
if (responseItem instanceof Object && responseItem.name) {
|
|
44
|
+
//Name already in hashed form
|
|
45
|
+
if (responseItem.name && responseItem.name.includes('[')) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const hashedName = namehash(responseItem.name)
|
|
50
|
+
if (responseItem.id !== hashedName) {
|
|
51
|
+
this.update({ ...responseItem, name: hashedName, invalidName: true })
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
return response
|
|
56
|
+
}
|
|
57
|
+
|
|
1
58
|
export default class GqlManager {
|
|
2
59
|
public gql: any = () => null
|
|
3
60
|
public client?: any | null = null
|
|
4
61
|
|
|
5
62
|
public setUrl = async (url: string | null) => {
|
|
6
63
|
if (url) {
|
|
7
|
-
const imported = await
|
|
8
|
-
|
|
64
|
+
const [imported, traverse, { visit, parse }] = await Promise.all([
|
|
65
|
+
import('graphql-request'),
|
|
66
|
+
import('traverse'),
|
|
67
|
+
import('graphql/language'),
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
this.client = new imported.GraphQLClient(url, {
|
|
71
|
+
requestMiddleware: requestMiddleware(visit, parse),
|
|
72
|
+
responseMiddleware: responseMiddleware(traverse.default),
|
|
73
|
+
})
|
|
9
74
|
this.gql = imported.gql
|
|
10
75
|
} else {
|
|
11
76
|
this.client = null
|
package/src/utils/normalise.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { concat, hexlify, keccak256, toUtf8Bytes } from 'ethers/lib/utils'
|
|
2
2
|
import uts46 from 'idna-uts46-hx/uts46bundle.js'
|
|
3
|
+
import { decodeLabelhash, isEncodedLabelhash } from './labels'
|
|
3
4
|
|
|
4
5
|
const zeros = new Uint8Array(32)
|
|
5
6
|
zeros.fill(0)
|
|
@@ -7,16 +8,21 @@ zeros.fill(0)
|
|
|
7
8
|
export const normalise = (name: string) =>
|
|
8
9
|
name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name
|
|
9
10
|
|
|
10
|
-
export const namehash = (
|
|
11
|
+
export const namehash = (name: string): string => {
|
|
11
12
|
let result: string | Uint8Array = zeros
|
|
12
13
|
|
|
13
|
-
const name = normalise(inputName)
|
|
14
|
-
|
|
15
14
|
if (name) {
|
|
16
15
|
const labels = name.split('.')
|
|
17
16
|
|
|
18
17
|
for (var i = labels.length - 1; i >= 0; i--) {
|
|
19
|
-
|
|
18
|
+
let labelSha: string
|
|
19
|
+
if (isEncodedLabelhash(labels[i])) {
|
|
20
|
+
labelSha = decodeLabelhash(labels[i])
|
|
21
|
+
} else {
|
|
22
|
+
const normalised = normalise(labels[i])
|
|
23
|
+
labelSha = keccak256(toUtf8Bytes(normalised))
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
result = keccak256(concat([result, labelSha]))
|
|
21
27
|
}
|
|
22
28
|
} else {
|