@ensdomains/ensjs 3.0.0-alpha.52 → 3.0.0-alpha.54

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.
@@ -23,11 +23,11 @@ __export(getProfile_exports, {
23
23
  module.exports = __toCommonJS(getProfile_exports);
24
24
  var import_address_encoder = require("@ensdomains/address-encoder");
25
25
  var import_abi = require("@ethersproject/abi");
26
+ var import_address = require("@ethersproject/address");
26
27
  var import_bytes = require("@ethersproject/bytes");
27
28
  var import_contentHash = require("../utils/contentHash");
28
29
  var import_hexEncodedName = require("../utils/hexEncodedName");
29
30
  var import_normalise = require("../utils/normalise");
30
- var import_validation = require("../utils/validation");
31
31
  const makeMulticallData = async ({
32
32
  _getAddr,
33
33
  _getContentHash,
@@ -468,11 +468,8 @@ async function getProfile_default({
468
468
  options.fallback.coinTypes = options.fallback.coinTypes.map(mapCoinTypes);
469
469
  }
470
470
  }
471
- const inputType = (0, import_validation.parseInputType)(nameOrAddress);
472
- if (inputType.type === "unknown" || inputType.info === "unsupported") {
473
- throw new Error("Invalid input type");
474
- }
475
- if (inputType.type === "address") {
471
+ const inputIsAddress = (0, import_address.isAddress)(nameOrAddress);
472
+ if (inputIsAddress) {
476
473
  return getProfileFromAddress(
477
474
  {
478
475
  contracts,
@@ -32,7 +32,6 @@ __export(initialGetters_exports, {
32
32
  getOwner: () => import_getOwner.default,
33
33
  getPrice: () => import_getPrice.default,
34
34
  getProfile: () => import_getProfile.default,
35
- getRecords: () => import_getRecords.default,
36
35
  getSubnames: () => import_getSubnames.default,
37
36
  supportsTLD: () => import_supportsTLD.default
38
37
  });
@@ -45,7 +44,6 @@ var import_getNames = __toESM(require("./getNames"));
45
44
  var import_getOwner = __toESM(require("./getOwner"));
46
45
  var import_getPrice = __toESM(require("./getPrice"));
47
46
  var import_getProfile = __toESM(require("./getProfile"));
48
- var import_getRecords = __toESM(require("./getRecords"));
49
47
  __reExport(initialGetters_exports, require("./getSpecificRecord"), module.exports);
50
48
  var import_getSubnames = __toESM(require("./getSubnames"));
51
49
  var import_supportsTLD = __toESM(require("./supportsTLD"));
package/dist/cjs/index.js CHANGED
@@ -188,11 +188,6 @@ class ENS {
188
188
  ],
189
189
  "getProfile"
190
190
  );
191
- this.getRecords = this.generateFunction(
192
- "initialGetters",
193
- ["getProfile"],
194
- "getRecords"
195
- );
196
191
  this.getName = this.generateRawFunction(
197
192
  "initialGetters",
198
193
  ["contracts"],
@@ -19,8 +19,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var consts_exports = {};
20
20
  __export(consts_exports, {
21
21
  EMPTY_ADDRESS: () => EMPTY_ADDRESS,
22
- MAX_INT_64: () => MAX_INT_64
22
+ MAX_INT_64: () => MAX_INT_64,
23
+ MINIMUM_DOT_ETH_CHARS: () => MINIMUM_DOT_ETH_CHARS
23
24
  });
24
25
  module.exports = __toCommonJS(consts_exports);
25
26
  const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
26
27
  const MAX_INT_64 = BigInt("18446744073709551615");
28
+ const MINIMUM_DOT_ETH_CHARS = 3;
@@ -19,70 +19,64 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var validation_exports = {};
20
20
  __export(validation_exports, {
21
21
  checkIsDotEth: () => checkIsDotEth,
22
- parseInputType: () => parseInputType,
23
- validateName: () => validateName,
24
- validateTLD: () => validateTLD
22
+ parseInput: () => parseInput,
23
+ validateName: () => validateName
25
24
  });
26
25
  module.exports = __toCommonJS(validation_exports);
27
- var import_address = require("@ethersproject/address");
26
+ var import_consts = require("./consts");
28
27
  var import_labels = require("./labels");
29
28
  var import_normalise = require("./normalise");
30
29
  const validateName = (name) => {
31
30
  const nameArray = name.split(".");
32
- const hasEmptyLabels = nameArray.some((label) => label.length === 0);
33
- if (hasEmptyLabels)
34
- throw new Error("Name cannot have empty labels");
35
- const normalizedArray = nameArray.map((label) => {
31
+ const normalisedArray = nameArray.map((label) => {
32
+ if (label.length === 0)
33
+ throw new Error("Name cannot have empty labels");
36
34
  if (label === "[root]") {
35
+ if (name !== label)
36
+ throw new Error("Root label must be the only label");
37
37
  return label;
38
38
  }
39
- return (0, import_labels.isEncodedLabelhash)(label) ? label : (0, import_normalise.normalise)(label);
39
+ return (0, import_labels.isEncodedLabelhash)(label) ? (0, import_labels.checkLabel)(label) || label : (0, import_normalise.normalise)(label);
40
40
  });
41
- const normalizedName = normalizedArray.join(".");
42
- (0, import_labels.saveName)(normalizedName);
43
- return normalizedName;
41
+ const normalisedName = normalisedArray.join(".");
42
+ (0, import_labels.saveName)(normalisedName);
43
+ return normalisedName;
44
44
  };
45
- const validateTLD = (name) => {
46
- const labels = name.split(".");
47
- return validateName(labels[labels.length - 1]);
48
- };
49
- const parseInputType = (input) => {
50
- const validTLD = validateTLD(input);
51
- const regex = /[^.]+$/;
45
+ const parseInput = (input) => {
46
+ var _a;
47
+ let nameReference = input;
48
+ let isValid = false;
52
49
  try {
53
- validateName(input);
54
- } catch (e) {
55
- return {
56
- type: "unknown"
57
- };
58
- }
59
- if (input.indexOf(".") !== -1) {
60
- const termArray = input.split(".");
61
- const tld = input.match(regex) ? input.match(regex)[0] : "";
62
- if (validTLD) {
63
- if (tld === "eth" && [...termArray[termArray.length - 2]].length < 3) {
64
- return {
65
- type: "name",
66
- info: "short"
67
- };
68
- }
69
- return {
70
- type: "name",
71
- info: "supported"
72
- };
73
- }
74
- return {
75
- type: "name",
76
- info: "unsupported"
77
- };
50
+ nameReference = validateName(input);
51
+ isValid = true;
52
+ } catch {
78
53
  }
79
- if ((0, import_address.isAddress)(input)) {
54
+ const normalisedName = isValid ? nameReference : void 0;
55
+ const labels = nameReference.split(".");
56
+ const tld = labels[labels.length - 1];
57
+ const isETH = tld === "eth";
58
+ const labelDataArray = (0, import_normalise.split)(nameReference);
59
+ const isShort = (((_a = labelDataArray[0].output) == null ? void 0 : _a.length) || 0) < import_consts.MINIMUM_DOT_ETH_CHARS;
60
+ if (labels.length === 1) {
80
61
  return {
81
- type: "address"
62
+ type: "label",
63
+ normalised: normalisedName,
64
+ isShort,
65
+ isValid,
66
+ is2LD: false,
67
+ isETH,
68
+ labelDataArray
82
69
  };
83
70
  }
71
+ const is2LD = labels.length === 2;
84
72
  return {
85
- type: "label"
73
+ type: "name",
74
+ normalised: normalisedName,
75
+ isShort: isETH && is2LD ? isShort : false,
76
+ isValid,
77
+ is2LD,
78
+ isETH,
79
+ labelDataArray
86
80
  };
87
81
  };
88
82
  const checkIsDotEth = (labels) => labels.length === 2 && labels[1] === "eth";
@@ -1,11 +1,11 @@
1
1
  // src/functions/getProfile.ts
2
2
  import { formatsByName } from "@ensdomains/address-encoder";
3
3
  import { defaultAbiCoder } from "@ethersproject/abi";
4
+ import { isAddress } from "@ethersproject/address";
4
5
  import { hexStripZeros, isBytesLike } from "@ethersproject/bytes";
5
6
  import { decodeContenthash } from "../utils/contentHash.mjs";
6
7
  import { hexEncodeName } from "../utils/hexEncodedName.mjs";
7
8
  import { namehash } from "../utils/normalise.mjs";
8
- import { parseInputType } from "../utils/validation.mjs";
9
9
  var makeMulticallData = async ({
10
10
  _getAddr,
11
11
  _getContentHash,
@@ -446,11 +446,8 @@ async function getProfile_default({
446
446
  options.fallback.coinTypes = options.fallback.coinTypes.map(mapCoinTypes);
447
447
  }
448
448
  }
449
- const inputType = parseInputType(nameOrAddress);
450
- if (inputType.type === "unknown" || inputType.info === "unsupported") {
451
- throw new Error("Invalid input type");
452
- }
453
- if (inputType.type === "address") {
449
+ const inputIsAddress = isAddress(nameOrAddress);
450
+ if (inputIsAddress) {
454
451
  return getProfileFromAddress(
455
452
  {
456
453
  contracts,
@@ -7,10 +7,9 @@ import { default as default5 } from "./getNames.mjs";
7
7
  import { default as default6 } from "./getOwner.mjs";
8
8
  import { default as default7 } from "./getPrice.mjs";
9
9
  import { default as default8 } from "./getProfile.mjs";
10
- import { default as default9 } from "./getRecords.mjs";
11
10
  export * from "./getSpecificRecord.mjs";
12
- import { default as default10 } from "./getSubnames.mjs";
13
- import { default as default11 } from "./supportsTLD.mjs";
11
+ import { default as default9 } from "./getSubnames.mjs";
12
+ import { default as default10 } from "./supportsTLD.mjs";
14
13
  export {
15
14
  default2 as batch,
16
15
  default3 as getExpiry,
@@ -19,7 +18,6 @@ export {
19
18
  default6 as getOwner,
20
19
  default7 as getPrice,
21
20
  default8 as getProfile,
22
- default9 as getRecords,
23
- default10 as getSubnames,
24
- default11 as supportsTLD
21
+ default9 as getSubnames,
22
+ default10 as supportsTLD
25
23
  };
@@ -165,11 +165,6 @@ var ENS = class {
165
165
  ],
166
166
  "getProfile"
167
167
  );
168
- getRecords = this.generateFunction(
169
- "initialGetters",
170
- ["getProfile"],
171
- "getRecords"
172
- );
173
168
  getName = this.generateRawFunction(
174
169
  "initialGetters",
175
170
  ["contracts"],
@@ -1,7 +1,9 @@
1
1
  // src/utils/consts.ts
2
2
  var EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
3
3
  var MAX_INT_64 = BigInt("18446744073709551615");
4
+ var MINIMUM_DOT_ETH_CHARS = 3;
4
5
  export {
5
6
  EMPTY_ADDRESS,
6
- MAX_INT_64
7
+ MAX_INT_64,
8
+ MINIMUM_DOT_ETH_CHARS
7
9
  };
@@ -1,69 +1,62 @@
1
1
  // src/utils/validation.ts
2
- import { isAddress } from "@ethersproject/address";
3
- import { isEncodedLabelhash, saveName } from "./labels.mjs";
4
- import { normalise } from "./normalise.mjs";
2
+ import { MINIMUM_DOT_ETH_CHARS } from "./consts.mjs";
3
+ import { checkLabel, isEncodedLabelhash, saveName } from "./labels.mjs";
4
+ import { normalise, split } from "./normalise.mjs";
5
5
  var validateName = (name) => {
6
6
  const nameArray = name.split(".");
7
- const hasEmptyLabels = nameArray.some((label) => label.length === 0);
8
- if (hasEmptyLabels)
9
- throw new Error("Name cannot have empty labels");
10
- const normalizedArray = nameArray.map((label) => {
7
+ const normalisedArray = nameArray.map((label) => {
8
+ if (label.length === 0)
9
+ throw new Error("Name cannot have empty labels");
11
10
  if (label === "[root]") {
11
+ if (name !== label)
12
+ throw new Error("Root label must be the only label");
12
13
  return label;
13
14
  }
14
- return isEncodedLabelhash(label) ? label : normalise(label);
15
+ return isEncodedLabelhash(label) ? checkLabel(label) || label : normalise(label);
15
16
  });
16
- const normalizedName = normalizedArray.join(".");
17
- saveName(normalizedName);
18
- return normalizedName;
17
+ const normalisedName = normalisedArray.join(".");
18
+ saveName(normalisedName);
19
+ return normalisedName;
19
20
  };
20
- var validateTLD = (name) => {
21
- const labels = name.split(".");
22
- return validateName(labels[labels.length - 1]);
23
- };
24
- var parseInputType = (input) => {
25
- const validTLD = validateTLD(input);
26
- const regex = /[^.]+$/;
21
+ var parseInput = (input) => {
22
+ let nameReference = input;
23
+ let isValid = false;
27
24
  try {
28
- validateName(input);
29
- } catch (e) {
30
- return {
31
- type: "unknown"
32
- };
33
- }
34
- if (input.indexOf(".") !== -1) {
35
- const termArray = input.split(".");
36
- const tld = input.match(regex) ? input.match(regex)[0] : "";
37
- if (validTLD) {
38
- if (tld === "eth" && [...termArray[termArray.length - 2]].length < 3) {
39
- return {
40
- type: "name",
41
- info: "short"
42
- };
43
- }
44
- return {
45
- type: "name",
46
- info: "supported"
47
- };
48
- }
49
- return {
50
- type: "name",
51
- info: "unsupported"
52
- };
25
+ nameReference = validateName(input);
26
+ isValid = true;
27
+ } catch {
53
28
  }
54
- if (isAddress(input)) {
29
+ const normalisedName = isValid ? nameReference : void 0;
30
+ const labels = nameReference.split(".");
31
+ const tld = labels[labels.length - 1];
32
+ const isETH = tld === "eth";
33
+ const labelDataArray = split(nameReference);
34
+ const isShort = (labelDataArray[0].output?.length || 0) < MINIMUM_DOT_ETH_CHARS;
35
+ if (labels.length === 1) {
55
36
  return {
56
- type: "address"
37
+ type: "label",
38
+ normalised: normalisedName,
39
+ isShort,
40
+ isValid,
41
+ is2LD: false,
42
+ isETH,
43
+ labelDataArray
57
44
  };
58
45
  }
46
+ const is2LD = labels.length === 2;
59
47
  return {
60
- type: "label"
48
+ type: "name",
49
+ normalised: normalisedName,
50
+ isShort: isETH && is2LD ? isShort : false,
51
+ isValid,
52
+ is2LD,
53
+ isETH,
54
+ labelDataArray
61
55
  };
62
56
  };
63
57
  var checkIsDotEth = (labels) => labels.length === 2 && labels[1] === "eth";
64
58
  export {
65
59
  checkIsDotEth,
66
- parseInputType,
67
- validateName,
68
- validateTLD
60
+ parseInput,
61
+ validateName
69
62
  };
@@ -6,7 +6,6 @@ export { default as getNames } from './getNames';
6
6
  export { default as getOwner } from './getOwner';
7
7
  export { default as getPrice } from './getPrice';
8
8
  export { default as getProfile } from './getProfile';
9
- export { default as getRecords } from './getRecords';
10
9
  export * from './getSpecificRecord';
11
10
  export { default as getSubnames } from './getSubnames';
12
11
  export { default as supportsTLD } from './supportsTLD';
@@ -13,7 +13,6 @@ import type getNames from './getNames';
13
13
  import type getOwner from './getOwner';
14
14
  import type getPrice from './getPrice';
15
15
  import type getProfile from './getProfile';
16
- import type getRecords from './getRecords';
17
16
  import type getResolver from './getResolver';
18
17
  import type { getABI, getAddr, getContentHash, getText, _getABI, _getAddr, _getContentHash, _getText } from './getSpecificRecord';
19
18
  import type getSubnames from './getSubnames';
@@ -53,7 +52,6 @@ declare type Function = {
53
52
  getOwner: typeof getOwner;
54
53
  getPrice: typeof getPrice;
55
54
  getProfile: typeof getProfile;
56
- getRecords: typeof getRecords;
57
55
  getResolver: typeof getResolver;
58
56
  getAddr: typeof getAddr;
59
57
  getContentHash: typeof getContentHash;
@@ -182,38 +182,6 @@ export declare class ENS {
182
182
  isInvalidResolverAddress?: boolean | undefined;
183
183
  reverseResolverAddress?: string | undefined;
184
184
  } | undefined>;
185
- getRecords: (name: string, options?: {
186
- contentHash?: boolean | undefined;
187
- texts?: boolean | string[] | undefined;
188
- coinTypes?: boolean | string[] | undefined;
189
- resolverAddress?: string | undefined;
190
- } | undefined) => Promise<{
191
- isMigrated: boolean | null;
192
- createdAt: string | null;
193
- address?: string | undefined;
194
- name?: string | null | undefined;
195
- decryptedName?: string | null | undefined;
196
- match?: boolean | undefined;
197
- message?: string | undefined;
198
- records?: {
199
- contentHash?: string | import("./utils/contentHash").DecodedContentHash | null | undefined;
200
- texts?: {
201
- key: string | number;
202
- type: "text" | "addr" | "contentHash";
203
- coin?: string | undefined;
204
- value: string;
205
- }[] | undefined;
206
- coinTypes?: {
207
- key: string | number;
208
- type: "text" | "addr" | "contentHash";
209
- coin?: string | undefined;
210
- value: string;
211
- }[] | undefined;
212
- } | undefined;
213
- resolverAddress?: string | undefined;
214
- isInvalidResolverAddress?: boolean | undefined;
215
- reverseResolverAddress?: string | undefined;
216
- } | undefined>;
217
185
  getName: GeneratedRawFunction<{
218
186
  raw: ({ contracts }: ENSArgs<"contracts">, address: string) => Promise<{
219
187
  to: string;
@@ -1,2 +1,3 @@
1
1
  export declare const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
2
2
  export declare const MAX_INT_64: bigint;
3
+ export declare const MINIMUM_DOT_ETH_CHARS = 3;
@@ -1,9 +1,13 @@
1
+ import { Label } from './normalise';
1
2
  export declare const validateName: (name: string) => string;
2
- export declare const validateTLD: (name: string) => string;
3
- declare type InputType = {
4
- type: 'name' | 'label' | 'address' | 'unknown';
5
- info?: 'short' | 'supported' | 'unsupported';
3
+ export declare type ParsedInputResult = {
4
+ type: 'name' | 'label';
5
+ normalised: string | undefined;
6
+ isValid: boolean;
7
+ isShort: boolean;
8
+ is2LD: boolean;
9
+ isETH: boolean;
10
+ labelDataArray: Label[];
6
11
  };
7
- export declare const parseInputType: (input: string) => InputType;
12
+ export declare const parseInput: (input: string) => ParsedInputResult;
8
13
  export declare const checkIsDotEth: (labels: string[]) => boolean;
9
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensdomains/ensjs",
3
- "version": "3.0.0-alpha.52",
3
+ "version": "3.0.0-alpha.54",
4
4
  "description": "ENS javascript library for contract interaction",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.mjs",
@@ -62,8 +62,7 @@
62
62
  "@adraffy/ens-normalize": "1.9.0",
63
63
  "@ensdomains/address-encoder": "^0.2.18",
64
64
  "@ensdomains/content-hash": "^2.5.7",
65
- "@ensdomains/dnsprovejs": "^0.4.1",
66
- "@ensdomains/dnssecoraclejs": "^0.2.7",
65
+ "@ensdomains/dnssecoraclejs": "^0.2.8",
67
66
  "cbor": "^8.1.0",
68
67
  "dns-packet": "^5.3.1",
69
68
  "graphql": "^16.3.0",
@@ -1,11 +1,11 @@
1
1
  import { formatsByName } from '@ensdomains/address-encoder'
2
2
  import { defaultAbiCoder } from '@ethersproject/abi'
3
+ import { isAddress } from '@ethersproject/address'
3
4
  import { hexStripZeros, isBytesLike } from '@ethersproject/bytes'
4
5
  import { ENSArgs } from '..'
5
6
  import { decodeContenthash, DecodedContentHash } from '../utils/contentHash'
6
7
  import { hexEncodeName } from '../utils/hexEncodedName'
7
8
  import { namehash } from '../utils/normalise'
8
- import { parseInputType } from '../utils/validation'
9
9
 
10
10
  type InternalProfileOptions = {
11
11
  contentHash?: boolean | string | DecodedContentHash
@@ -653,13 +653,9 @@ export default async function (
653
653
  }
654
654
  }
655
655
 
656
- const inputType = parseInputType(nameOrAddress)
656
+ const inputIsAddress = isAddress(nameOrAddress)
657
657
 
658
- if (inputType.type === 'unknown' || inputType.info === 'unsupported') {
659
- throw new Error('Invalid input type')
660
- }
661
-
662
- if (inputType.type === 'address') {
658
+ if (inputIsAddress) {
663
659
  return getProfileFromAddress(
664
660
  {
665
661
  contracts,
@@ -6,7 +6,6 @@ export { default as getNames } from './getNames'
6
6
  export { default as getOwner } from './getOwner'
7
7
  export { default as getPrice } from './getPrice'
8
8
  export { default as getProfile } from './getProfile'
9
- export { default as getRecords } from './getRecords'
10
9
  export * from './getSpecificRecord'
11
10
  export { default as getSubnames } from './getSubnames'
12
11
  export { default as supportsTLD } from './supportsTLD'
@@ -17,7 +17,6 @@ import type getNames from './getNames'
17
17
  import type getOwner from './getOwner'
18
18
  import type getPrice from './getPrice'
19
19
  import type getProfile from './getProfile'
20
- import type getRecords from './getRecords'
21
20
  import type getResolver from './getResolver'
22
21
  import type {
23
22
  getABI,
@@ -71,7 +70,6 @@ type Function = {
71
70
  getOwner: typeof getOwner
72
71
  getPrice: typeof getPrice
73
72
  getProfile: typeof getProfile
74
- getRecords: typeof getRecords
75
73
  getResolver: typeof getResolver
76
74
  getAddr: typeof getAddr
77
75
  getContentHash: typeof getContentHash
package/src/index.ts CHANGED
@@ -431,12 +431,6 @@ export class ENS {
431
431
  'getProfile',
432
432
  )
433
433
 
434
- public getRecords = this.generateFunction<FunctionTypes['getRecords']>(
435
- 'initialGetters',
436
- ['getProfile'],
437
- 'getRecords',
438
- )
439
-
440
434
  public getName = this.generateRawFunction<FunctionTypes['getName']>(
441
435
  'initialGetters',
442
436
  ['contracts'],
@@ -1,2 +1,3 @@
1
1
  export const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'
2
2
  export const MAX_INT_64 = BigInt('18446744073709551615')
3
+ export const MINIMUM_DOT_ETH_CHARS = 3
@@ -0,0 +1,164 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ import { parseInput, validateName } from './validation'
3
+
4
+ declare namespace localStorage {
5
+ const getItem: jest.MockedFn<Storage['getItem']>
6
+ const setItem: jest.MockedFn<Storage['setItem']>
7
+ const removeItem: jest.MockedFn<Storage['removeItem']>
8
+ }
9
+
10
+ const labelsMock = {
11
+ '0x68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87':
12
+ 'something',
13
+ '0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0': 'eth',
14
+ }
15
+
16
+ const labelsMockJSON = JSON.stringify(labelsMock)
17
+
18
+ describe('validateName()', () => {
19
+ beforeEach(() => {
20
+ localStorage.getItem.mockClear()
21
+ localStorage.setItem.mockClear()
22
+ localStorage.getItem.mockImplementation(() => labelsMockJSON)
23
+ })
24
+ it('should throw if the name has an empty label', () => {
25
+ expect(() => validateName('foo..bar')).toThrowError(
26
+ 'Name cannot have empty labels',
27
+ )
28
+ expect(() => validateName('.foo.bar')).toThrowError(
29
+ 'Name cannot have empty labels',
30
+ )
31
+ expect(() => validateName('foo.bar.')).toThrowError(
32
+ 'Name cannot have empty labels',
33
+ )
34
+ })
35
+ it('should allow names with [root] as a label', () => {
36
+ expect(validateName('[root]')).toEqual('[root]')
37
+ })
38
+ it('should throw if the name has [root] as a label and is not the only label', () => {
39
+ expect(() => validateName('foo.[root].bar')).toThrowError(
40
+ 'Root label must be the only label',
41
+ )
42
+ })
43
+
44
+ it('should get the decoded label hash from local storage', () => {
45
+ expect(
46
+ validateName(
47
+ 'thing.[68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87].eth',
48
+ ),
49
+ ).toEqual('thing.something.eth')
50
+ expect(localStorage.getItem).toHaveBeenCalled()
51
+ })
52
+ it('should fallback to encoded label hash if the decoded label hash is not in local storage', () => {
53
+ expect(
54
+ validateName(
55
+ 'something.[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17].eth',
56
+ ),
57
+ ).toEqual(
58
+ 'something.[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17].eth',
59
+ )
60
+ expect(localStorage.getItem).toHaveBeenCalled()
61
+ expect(
62
+ validateName(
63
+ '[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17]',
64
+ ),
65
+ ).toEqual(
66
+ '[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17]',
67
+ )
68
+ })
69
+ it('should normalise the name', () => {
70
+ expect(validateName('aAaaA.eth')).toEqual('aaaaa.eth')
71
+ })
72
+ it('should save the normalised name to local storage', () => {
73
+ validateName('swAgCity.eth')
74
+ expect(localStorage.setItem).toHaveBeenCalledWith(
75
+ 'ensjs:labels',
76
+ JSON.stringify({
77
+ ...labelsMock,
78
+ '0xee3bbc5c1db3f1dbd5ec4cdfd627fb76aba6215d814354b6c0f8d74253adf1a8':
79
+ 'swagcity',
80
+ }),
81
+ )
82
+ })
83
+ it('should return the normalised name', () => {
84
+ expect(validateName('swAgCity.eth')).toEqual('swagcity.eth')
85
+ })
86
+ })
87
+
88
+ describe('parseInput()', () => {
89
+ it('should parse the input', () => {
90
+ expect(parseInput('bar.eth')).toEqual({
91
+ type: 'name',
92
+ normalised: 'bar.eth',
93
+ isShort: false,
94
+ isValid: true,
95
+ is2LD: true,
96
+ isETH: true,
97
+ labelDataArray: expect.any(Array),
98
+ })
99
+ })
100
+ it('should return a normalised name', () => {
101
+ expect(parseInput('bAr.etH').normalised).toEqual('bar.eth')
102
+ })
103
+ it('should parse the input if it is invalid', () => {
104
+ expect(parseInput('bar..eth')).toEqual({
105
+ type: 'name',
106
+ normalised: undefined,
107
+ isShort: false,
108
+ isValid: false,
109
+ is2LD: false,
110
+ isETH: true,
111
+ labelDataArray: expect.any(Array),
112
+ })
113
+ })
114
+ it('should return type as label if input is a label', () => {
115
+ expect(parseInput('bar').type).toEqual('label')
116
+ })
117
+ describe('should return correct value', () => {
118
+ describe('isShort', () => {
119
+ it('should return true if input is label and less than 3 characters', () => {
120
+ expect(parseInput('ba').isShort).toEqual(true)
121
+ })
122
+ it('should return true if input is less than 3 char emoji', () => {
123
+ expect(parseInput('🇺🇸').isShort).toEqual(true)
124
+ })
125
+ it('should return false if input is 3 char emoji', () => {
126
+ expect(parseInput('🏳️‍🌈').isShort).toEqual(false)
127
+ })
128
+ it('should return false if input is label and 3 characters', () => {
129
+ expect(parseInput('bar').isShort).toEqual(false)
130
+ })
131
+ it('should return true if input is 2LD .eth name and label is less than 3 characters', () => {
132
+ expect(parseInput('ba.eth').isShort).toEqual(true)
133
+ })
134
+ it('should return false if input is 2LD .eth name and label is 3 characters', () => {
135
+ expect(parseInput('bar.eth').isShort).toEqual(false)
136
+ })
137
+ it('should return false if input is 2LD other name and label is less than 3 characters', () => {
138
+ expect(parseInput('ba.com').isShort).toEqual(false)
139
+ })
140
+ it('should return false if input is 3LD .eth name and label is less than 3 characters', () => {
141
+ expect(parseInput('ba.bar.eth').isShort).toEqual(false)
142
+ })
143
+ })
144
+ describe('is2LD', () => {
145
+ it('should return true if input is 2LD name', () => {
146
+ expect(parseInput('bar.eth').is2LD).toEqual(true)
147
+ })
148
+ it('should return false if input is 3LD name', () => {
149
+ expect(parseInput('bar.foo.eth').is2LD).toEqual(false)
150
+ })
151
+ it('should return false if input is label', () => {
152
+ expect(parseInput('bar').is2LD).toEqual(false)
153
+ })
154
+ })
155
+ describe('isETH', () => {
156
+ it('should return true if input is .eth name', () => {
157
+ expect(parseInput('bar.eth').isETH).toEqual(true)
158
+ })
159
+ it('should return false if input is other name', () => {
160
+ expect(parseInput('bar.com').isETH).toEqual(false)
161
+ })
162
+ })
163
+ })
164
+ })
@@ -1,73 +1,73 @@
1
- import { isAddress } from '@ethersproject/address'
2
- import { isEncodedLabelhash, saveName } from './labels'
3
- import { normalise } from './normalise'
1
+ import { MINIMUM_DOT_ETH_CHARS } from './consts'
2
+ import { checkLabel, isEncodedLabelhash, saveName } from './labels'
3
+ import { Label, normalise, split } from './normalise'
4
4
 
5
5
  export const validateName = (name: string) => {
6
6
  const nameArray = name.split('.')
7
- const hasEmptyLabels = nameArray.some((label) => label.length === 0)
8
- if (hasEmptyLabels) throw new Error('Name cannot have empty labels')
9
- const normalizedArray = nameArray.map((label) => {
7
+ const normalisedArray = nameArray.map((label) => {
8
+ if (label.length === 0) throw new Error('Name cannot have empty labels')
10
9
  if (label === '[root]') {
10
+ if (name !== label) throw new Error('Root label must be the only label')
11
11
  return label
12
12
  }
13
- return isEncodedLabelhash(label) ? label : normalise(label)
13
+ return isEncodedLabelhash(label)
14
+ ? checkLabel(label) || label
15
+ : normalise(label)
14
16
  })
15
- const normalizedName = normalizedArray.join('.')
16
- saveName(normalizedName)
17
- return normalizedName
17
+ const normalisedName = normalisedArray.join('.')
18
+ saveName(normalisedName)
19
+ return normalisedName
18
20
  }
19
21
 
20
- export const validateTLD = (name: string) => {
21
- const labels = name.split('.')
22
- return validateName(labels[labels.length - 1])
22
+ export type ParsedInputResult = {
23
+ type: 'name' | 'label'
24
+ normalised: string | undefined
25
+ isValid: boolean
26
+ isShort: boolean
27
+ is2LD: boolean
28
+ isETH: boolean
29
+ labelDataArray: Label[]
23
30
  }
24
31
 
25
- type InputType = {
26
- type: 'name' | 'label' | 'address' | 'unknown'
27
- info?: 'short' | 'supported' | 'unsupported'
28
- }
29
-
30
- export const parseInputType = (input: string): InputType => {
31
- const validTLD = validateTLD(input)
32
- const regex = /[^.]+$/
32
+ export const parseInput = (input: string): ParsedInputResult => {
33
+ let nameReference = input
34
+ let isValid = false
33
35
 
34
36
  try {
35
- validateName(input)
36
- } catch (e) {
37
- return {
38
- type: 'unknown',
39
- }
40
- }
37
+ nameReference = validateName(input)
38
+ isValid = true
39
+ } catch {}
41
40
 
42
- if (input.indexOf('.') !== -1) {
43
- const termArray = input.split('.')
44
- const tld = input.match(regex) ? input.match(regex)![0] : ''
45
- if (validTLD) {
46
- if (tld === 'eth' && [...termArray[termArray.length - 2]].length < 3) {
47
- // code-point length
48
- return {
49
- type: 'name',
50
- info: 'short',
51
- }
52
- }
53
- return {
54
- type: 'name',
55
- info: 'supported',
56
- }
57
- }
41
+ const normalisedName = isValid ? nameReference : undefined
58
42
 
43
+ const labels = nameReference.split('.')
44
+ const tld = labels[labels.length - 1]
45
+ const isETH = tld === 'eth'
46
+ const labelDataArray = split(nameReference)
47
+ const isShort =
48
+ (labelDataArray[0].output?.length || 0) < MINIMUM_DOT_ETH_CHARS
49
+
50
+ if (labels.length === 1) {
59
51
  return {
60
- type: 'name',
61
- info: 'unsupported',
62
- }
63
- }
64
- if (isAddress(input)) {
65
- return {
66
- type: 'address',
52
+ type: 'label',
53
+ normalised: normalisedName,
54
+ isShort,
55
+ isValid,
56
+ is2LD: false,
57
+ isETH,
58
+ labelDataArray,
67
59
  }
68
60
  }
61
+
62
+ const is2LD = labels.length === 2
69
63
  return {
70
- type: 'label',
64
+ type: 'name',
65
+ normalised: normalisedName,
66
+ isShort: isETH && is2LD ? isShort : false,
67
+ isValid,
68
+ is2LD,
69
+ isETH,
70
+ labelDataArray,
71
71
  }
72
72
  }
73
73
 
@@ -1,31 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var getRecords_exports = {};
20
- __export(getRecords_exports, {
21
- default: () => getRecords_default
22
- });
23
- module.exports = __toCommonJS(getRecords_exports);
24
- var import_validation = require("../utils/validation");
25
- async function getRecords_default({ getProfile }, name, options) {
26
- const inputType = (0, import_validation.parseInputType)(name);
27
- if (inputType.type !== "name" && inputType.type !== "label") {
28
- throw new Error("Input must be an ENS name");
29
- }
30
- return getProfile(name, options);
31
- }
@@ -1,12 +0,0 @@
1
- // src/functions/getRecords.ts
2
- import { parseInputType } from "../utils/validation.mjs";
3
- async function getRecords_default({ getProfile }, name, options) {
4
- const inputType = parseInputType(name);
5
- if (inputType.type !== "name" && inputType.type !== "label") {
6
- throw new Error("Input must be an ENS name");
7
- }
8
- return getProfile(name, options);
9
- }
10
- export {
11
- getRecords_default as default
12
- };
@@ -1,35 +0,0 @@
1
- import { ENSArgs } from '..';
2
- declare type ProfileOptions = {
3
- contentHash?: boolean;
4
- texts?: boolean | string[];
5
- coinTypes?: boolean | string[];
6
- resolverAddress?: string;
7
- };
8
- export default function ({ getProfile }: ENSArgs<'getProfile'>, name: string, options?: ProfileOptions): Promise<{
9
- isMigrated: boolean | null;
10
- createdAt: string | null;
11
- address?: string | undefined;
12
- name?: string | null | undefined;
13
- decryptedName?: string | null | undefined;
14
- match?: boolean | undefined;
15
- message?: string | undefined;
16
- records?: {
17
- contentHash?: string | import("../utils/contentHash").DecodedContentHash | null | undefined;
18
- texts?: {
19
- key: string | number;
20
- type: "text" | "addr" | "contentHash";
21
- coin?: string | undefined;
22
- value: string;
23
- }[] | undefined;
24
- coinTypes?: {
25
- key: string | number;
26
- type: "text" | "addr" | "contentHash";
27
- coin?: string | undefined;
28
- value: string;
29
- }[] | undefined;
30
- } | undefined;
31
- resolverAddress?: string | undefined;
32
- isInvalidResolverAddress?: boolean | undefined;
33
- reverseResolverAddress?: string | undefined;
34
- } | undefined>;
35
- export {};
@@ -1,23 +0,0 @@
1
- import { ENSArgs } from '..'
2
- import { parseInputType } from '../utils/validation'
3
-
4
- type ProfileOptions = {
5
- contentHash?: boolean
6
- texts?: boolean | string[]
7
- coinTypes?: boolean | string[]
8
- resolverAddress?: string
9
- }
10
-
11
- export default async function (
12
- { getProfile }: ENSArgs<'getProfile'>,
13
- name: string,
14
- options?: ProfileOptions,
15
- ) {
16
- const inputType = parseInputType(name)
17
-
18
- if (inputType.type !== 'name' && inputType.type !== 'label') {
19
- throw new Error('Input must be an ENS name')
20
- }
21
-
22
- return getProfile(name, options)
23
- }