@arcblock/did 1.29.18 → 1.29.19
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/esm/entity.d.mts +28 -0
- package/esm/entity.mjs +39 -0
- package/esm/index.d.mts +9 -1
- package/esm/index.mjs +10 -2
- package/esm/method.d.mts +30 -0
- package/esm/method.mjs +32 -0
- package/esm/name-registry.d.mts +31 -0
- package/esm/name-registry.mjs +37 -0
- package/esm/name-resolver.d.mts +41 -0
- package/esm/name-resolver.mjs +97 -0
- package/esm/name.d.mts +62 -0
- package/esm/name.mjs +73 -0
- package/esm/parse.d.mts +50 -0
- package/esm/parse.mjs +88 -0
- package/esm/short-form.d.mts +30 -0
- package/esm/short-form.mjs +46 -0
- package/esm/util.mjs +1 -1
- package/esm/validate.d.mts +23 -0
- package/esm/validate.mjs +36 -0
- package/lib/entity.cjs +40 -0
- package/lib/entity.d.cts +28 -0
- package/lib/index.cjs +31 -1
- package/lib/index.d.cts +9 -1
- package/lib/method.cjs +39 -0
- package/lib/method.d.cts +30 -0
- package/lib/name-registry.cjs +38 -0
- package/lib/name-registry.d.cts +31 -0
- package/lib/name-resolver.cjs +97 -0
- package/lib/name-resolver.d.cts +41 -0
- package/lib/name.cjs +77 -0
- package/lib/name.d.cts +62 -0
- package/lib/parse.cjs +91 -0
- package/lib/parse.d.cts +50 -0
- package/lib/short-form.cjs +47 -0
- package/lib/short-form.d.cts +30 -0
- package/lib/util.cjs +1 -1
- package/lib/validate.cjs +36 -0
- package/lib/validate.d.cts +23 -0
- package/package.json +3 -3
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/short-form.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Short-form DID Resolution
|
|
4
|
+
*
|
|
5
|
+
* Deterministic expansion of short-form identifiers to full DIDs.
|
|
6
|
+
* Based on base58 checksum validation (not heuristic guessing).
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* - Already a full DID (starts with "did:") → return as-is
|
|
10
|
+
* - Valid cryptographic address (passes isValid checksum) → did:abt:{address}
|
|
11
|
+
* - Everything else → did:name:{input}
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Expand a short-form identifier to a full DID.
|
|
15
|
+
*
|
|
16
|
+
* @param input - Short-form identifier (address, name, or full DID)
|
|
17
|
+
* @returns Full DID string
|
|
18
|
+
* @throws If input is not a non-empty string
|
|
19
|
+
*/
|
|
20
|
+
declare function expandShortForm(input: unknown): string;
|
|
21
|
+
/**
|
|
22
|
+
* Convert a full DID to its short form (strip the did:{method}: prefix).
|
|
23
|
+
*
|
|
24
|
+
* @param did - Full DID string
|
|
25
|
+
* @returns Short-form identifier
|
|
26
|
+
* @throws If input is not a non-empty string
|
|
27
|
+
*/
|
|
28
|
+
declare function toShortForm(did: unknown): string;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { expandShortForm, toShortForm };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { formatDid } from "./parse.mjs";
|
|
2
|
+
import { isValid } from "./index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/short-form.ts
|
|
5
|
+
/**
|
|
6
|
+
* Short-form DID Resolution
|
|
7
|
+
*
|
|
8
|
+
* Deterministic expansion of short-form identifiers to full DIDs.
|
|
9
|
+
* Based on base58 checksum validation (not heuristic guessing).
|
|
10
|
+
*
|
|
11
|
+
* Rules:
|
|
12
|
+
* - Already a full DID (starts with "did:") → return as-is
|
|
13
|
+
* - Valid cryptographic address (passes isValid checksum) → did:abt:{address}
|
|
14
|
+
* - Everything else → did:name:{input}
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Expand a short-form identifier to a full DID.
|
|
18
|
+
*
|
|
19
|
+
* @param input - Short-form identifier (address, name, or full DID)
|
|
20
|
+
* @returns Full DID string
|
|
21
|
+
* @throws If input is not a non-empty string
|
|
22
|
+
*/
|
|
23
|
+
function expandShortForm(input) {
|
|
24
|
+
if (typeof input !== "string") throw new Error("Input must be a string");
|
|
25
|
+
if (!input) throw new Error("Input cannot be empty");
|
|
26
|
+
if (input.startsWith("did:")) return input;
|
|
27
|
+
if (isValid(input)) return formatDid(input, "abt");
|
|
28
|
+
return formatDid(input, "name");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert a full DID to its short form (strip the did:{method}: prefix).
|
|
32
|
+
*
|
|
33
|
+
* @param did - Full DID string
|
|
34
|
+
* @returns Short-form identifier
|
|
35
|
+
* @throws If input is not a non-empty string
|
|
36
|
+
*/
|
|
37
|
+
function toShortForm(did) {
|
|
38
|
+
if (typeof did !== "string") throw new Error("Input must be a string");
|
|
39
|
+
if (!did) throw new Error("Input cannot be empty");
|
|
40
|
+
const match = did.match(/^did:[a-z0-9]+:(.+)$/);
|
|
41
|
+
if (match) return match[1];
|
|
42
|
+
return did;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { expandShortForm, toShortForm };
|
package/esm/util.mjs
CHANGED
|
@@ -12,7 +12,7 @@ const DID_PREFIX = "did:abt:";
|
|
|
12
12
|
const toBytes = (did) => {
|
|
13
13
|
try {
|
|
14
14
|
if (isHexStrict(did)) return Buffer.from(stripHexPrefix(did), "hex");
|
|
15
|
-
let bytes = fromBase58(did.replace(
|
|
15
|
+
let bytes = fromBase58(did.replace(/^did:[a-z0-9]+:/, ""));
|
|
16
16
|
while (bytes.length < 26) bytes = Buffer.concat([Uint8Array.from([0]), Uint8Array.from(bytes)]);
|
|
17
17
|
return bytes;
|
|
18
18
|
} catch (err) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/validate.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* DID Validation Functions
|
|
4
|
+
*
|
|
5
|
+
* isValid and isFromPublicKey remain in index.ts (too many dependencies to move).
|
|
6
|
+
* This file provides isKnownDid which covers all methods including did:name.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Check if a DID string is a known valid DID.
|
|
10
|
+
*
|
|
11
|
+
* Unlike `isValid` (which only validates cryptographic addresses),
|
|
12
|
+
* `isKnownDid` covers all known methods:
|
|
13
|
+
* - Crypto methods (abt/afs/aos/spaces): delegates to `isValid` for checksum validation
|
|
14
|
+
* - Alias methods (name): checks for non-empty identifier
|
|
15
|
+
* - Unknown methods: returns false
|
|
16
|
+
* - Bare cryptographic addresses: delegates to `isValid`
|
|
17
|
+
*
|
|
18
|
+
* @param did - DID string to validate
|
|
19
|
+
* @returns true if the DID is valid for its method
|
|
20
|
+
*/
|
|
21
|
+
declare function isKnownDid(did: unknown): boolean;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { isKnownDid };
|
package/esm/validate.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isAliasMethod, isCryptoMethod } from "./method.mjs";
|
|
2
|
+
import { isValid } from "./index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/validate.ts
|
|
5
|
+
/**
|
|
6
|
+
* DID Validation Functions
|
|
7
|
+
*
|
|
8
|
+
* isValid and isFromPublicKey remain in index.ts (too many dependencies to move).
|
|
9
|
+
* This file provides isKnownDid which covers all methods including did:name.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Check if a DID string is a known valid DID.
|
|
13
|
+
*
|
|
14
|
+
* Unlike `isValid` (which only validates cryptographic addresses),
|
|
15
|
+
* `isKnownDid` covers all known methods:
|
|
16
|
+
* - Crypto methods (abt/afs/aos/spaces): delegates to `isValid` for checksum validation
|
|
17
|
+
* - Alias methods (name): checks for non-empty identifier
|
|
18
|
+
* - Unknown methods: returns false
|
|
19
|
+
* - Bare cryptographic addresses: delegates to `isValid`
|
|
20
|
+
*
|
|
21
|
+
* @param did - DID string to validate
|
|
22
|
+
* @returns true if the DID is valid for its method
|
|
23
|
+
*/
|
|
24
|
+
function isKnownDid(did) {
|
|
25
|
+
if (typeof did !== "string" || !did) return false;
|
|
26
|
+
const match = did.match(/^did:([a-z0-9]+):(.*)$/);
|
|
27
|
+
if (!match) return isValid(did);
|
|
28
|
+
const [, method, identifier] = match;
|
|
29
|
+
if (!identifier) return false;
|
|
30
|
+
if (isCryptoMethod(method)) return isValid(did);
|
|
31
|
+
if (isAliasMethod(method)) return true;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { isKnownDid };
|
package/lib/entity.cjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const require_parse = require('./parse.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/entity.ts
|
|
4
|
+
/**
|
|
5
|
+
* Cross-Method Entity Association
|
|
6
|
+
*
|
|
7
|
+
* Functions for comparing DID identity across different methods.
|
|
8
|
+
* Two DIDs with different methods but the same identifier represent the same entity.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Get the entity identifier from a DID (strips method prefix).
|
|
12
|
+
* This is the canonical identity across methods.
|
|
13
|
+
*
|
|
14
|
+
* @param did - A full DID or bare address
|
|
15
|
+
* @returns The entity identifier
|
|
16
|
+
*/
|
|
17
|
+
function getEntityId(did) {
|
|
18
|
+
return require_parse.extractIdentifier(did);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check if two DIDs represent the same entity.
|
|
22
|
+
* Compares the identifier portion (case-insensitive), ignoring the method prefix.
|
|
23
|
+
*
|
|
24
|
+
* For did:name DIDs, performs exact string comparison (names are case-insensitive).
|
|
25
|
+
*
|
|
26
|
+
* @param a - First DID or address
|
|
27
|
+
* @param b - Second DID or address
|
|
28
|
+
* @returns true if both DIDs refer to the same entity
|
|
29
|
+
*/
|
|
30
|
+
function isSameEntity(a, b) {
|
|
31
|
+
if (typeof a !== "string" || typeof b !== "string" || !a || !b) return false;
|
|
32
|
+
const idA = require_parse.extractIdentifier(a);
|
|
33
|
+
const idB = require_parse.extractIdentifier(b);
|
|
34
|
+
if (!idA || !idB) return false;
|
|
35
|
+
return idA.toLowerCase() === idB.toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
exports.getEntityId = getEntityId;
|
|
40
|
+
exports.isSameEntity = isSameEntity;
|
package/lib/entity.d.cts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/entity.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Cross-Method Entity Association
|
|
4
|
+
*
|
|
5
|
+
* Functions for comparing DID identity across different methods.
|
|
6
|
+
* Two DIDs with different methods but the same identifier represent the same entity.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Get the entity identifier from a DID (strips method prefix).
|
|
10
|
+
* This is the canonical identity across methods.
|
|
11
|
+
*
|
|
12
|
+
* @param did - A full DID or bare address
|
|
13
|
+
* @returns The entity identifier
|
|
14
|
+
*/
|
|
15
|
+
declare function getEntityId(did: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Check if two DIDs represent the same entity.
|
|
18
|
+
* Compares the identifier portion (case-insensitive), ignoring the method prefix.
|
|
19
|
+
*
|
|
20
|
+
* For did:name DIDs, performs exact string comparison (names are case-insensitive).
|
|
21
|
+
*
|
|
22
|
+
* @param a - First DID or address
|
|
23
|
+
* @param b - Second DID or address
|
|
24
|
+
* @returns true if both DIDs refer to the same entity
|
|
25
|
+
*/
|
|
26
|
+
declare function isSameEntity(a: unknown, b: unknown): boolean;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { getEntityId, isSameEntity };
|
package/lib/index.cjs
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_method = require('./method.cjs');
|
|
3
|
+
const require_parse = require('./parse.cjs');
|
|
4
|
+
const require_entity = require('./entity.cjs');
|
|
2
5
|
const require_util = require('./util.cjs');
|
|
3
6
|
const require_type = require('./type.cjs');
|
|
7
|
+
const require_validate = require('./validate.cjs');
|
|
8
|
+
const require_short_form = require('./short-form.cjs');
|
|
9
|
+
const require_name = require('./name.cjs');
|
|
10
|
+
const require_name_registry = require('./name-registry.cjs');
|
|
11
|
+
const require_name_resolver = require('./name-resolver.cjs');
|
|
4
12
|
let _ocap_mcrypto = require("@ocap/mcrypto");
|
|
5
13
|
let _ocap_util = require("@ocap/util");
|
|
6
14
|
|
|
@@ -70,7 +78,7 @@ const fromHash = (hash, role = _ocap_mcrypto.types.RoleType.ROLE_ACCOUNT) => {
|
|
|
70
78
|
*/
|
|
71
79
|
const isFromPublicKey = (did, pk) => {
|
|
72
80
|
if (isValid(did) === false) return false;
|
|
73
|
-
return fromPublicKey(pk, require_type.toTypeInfo(did)) ===
|
|
81
|
+
return fromPublicKey(pk, require_type.toTypeInfo(did)) === (0, _ocap_util.toAddress)(did);
|
|
74
82
|
};
|
|
75
83
|
/**
|
|
76
84
|
* Check if a DID string is valid
|
|
@@ -93,22 +101,44 @@ const isValid = (did) => {
|
|
|
93
101
|
};
|
|
94
102
|
|
|
95
103
|
//#endregion
|
|
104
|
+
exports.ALIAS_METHODS = require_method.ALIAS_METHODS;
|
|
105
|
+
exports.ALL_METHODS = require_method.ALL_METHODS;
|
|
106
|
+
exports.CRYPTO_METHODS = require_method.CRYPTO_METHODS;
|
|
107
|
+
exports.DEFAULT_METHOD = require_method.DEFAULT_METHOD;
|
|
108
|
+
exports.DIDNameResolver = require_name_resolver.DIDNameResolver;
|
|
96
109
|
exports.DID_PREFIX = require_util.DID_PREFIX;
|
|
97
110
|
exports.DID_TYPE_ARCBLOCK = require_type.DID_TYPE_ARCBLOCK;
|
|
98
111
|
exports.DID_TYPE_ETHEREUM = require_type.DID_TYPE_ETHEREUM;
|
|
99
112
|
exports.DID_TYPE_PASSKEY = require_type.DID_TYPE_PASSKEY;
|
|
100
113
|
exports.DidType = require_type.DidType;
|
|
114
|
+
exports.InMemoryNameRegistry = require_name_registry.InMemoryNameRegistry;
|
|
115
|
+
exports.expandShortForm = require_short_form.expandShortForm;
|
|
116
|
+
exports.extractIdentifier = require_parse.extractIdentifier;
|
|
117
|
+
exports.extractMethod = require_parse.extractMethod;
|
|
118
|
+
exports.formatDid = require_parse.formatDid;
|
|
101
119
|
exports.fromHash = fromHash;
|
|
102
120
|
exports.fromPublicKey = fromPublicKey;
|
|
103
121
|
exports.fromPublicKeyHash = fromPublicKeyHash;
|
|
104
122
|
exports.fromSecretKey = fromSecretKey;
|
|
105
123
|
exports.fromTypeInfo = require_type.fromTypeInfo;
|
|
124
|
+
exports.getEntityId = require_entity.getEntityId;
|
|
125
|
+
exports.getResolveRoute = require_name.getResolveRoute;
|
|
126
|
+
exports.isAliasMethod = require_method.isAliasMethod;
|
|
127
|
+
exports.isCryptoMethod = require_method.isCryptoMethod;
|
|
106
128
|
exports.isEthereumDid = require_type.isEthereumDid;
|
|
107
129
|
exports.isEthereumType = require_type.isEthereumType;
|
|
108
130
|
exports.isFromPublicKey = isFromPublicKey;
|
|
131
|
+
exports.isGlobalName = require_name.isGlobalName;
|
|
132
|
+
exports.isKnownDid = require_validate.isKnownDid;
|
|
133
|
+
exports.isKnownMethod = require_method.isKnownMethod;
|
|
134
|
+
exports.isLocalName = require_name.isLocalName;
|
|
135
|
+
exports.isSameEntity = require_entity.isSameEntity;
|
|
109
136
|
exports.isValid = isValid;
|
|
137
|
+
exports.parse = require_parse.parse;
|
|
138
|
+
exports.parseNameHierarchy = require_name.parseNameHierarchy;
|
|
110
139
|
exports.toAddress = _ocap_util.toAddress;
|
|
111
140
|
exports.toDid = _ocap_util.toDid;
|
|
141
|
+
exports.toShortForm = require_short_form.toShortForm;
|
|
112
142
|
exports.toStrictHex = require_util.toStrictHex;
|
|
113
143
|
exports.toTypeInfo = require_type.toTypeInfo;
|
|
114
144
|
exports.toTypeInfoStr = require_type.toTypeInfoStr;
|
package/lib/index.d.cts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import { getEntityId, isSameEntity } from "./entity.cjs";
|
|
1
2
|
import { DIDType, DIDTypeArg, DIDTypeShortcut, DIDTypeStr, DID_TYPE_ARCBLOCK, DID_TYPE_ETHEREUM, DID_TYPE_PASSKEY, DidType, RequiredDIDType, fromTypeInfo, isEthereumDid, isEthereumType, toTypeInfo, toTypeInfoStr } from "./type.cjs";
|
|
2
3
|
import { DID_PREFIX, toStrictHex } from "./util.cjs";
|
|
4
|
+
import { ALIAS_METHODS, ALL_METHODS, AliasMethod, CRYPTO_METHODS, CryptoMethod, DEFAULT_METHOD, DIDMethod, isAliasMethod, isCryptoMethod, isKnownMethod } from "./method.cjs";
|
|
5
|
+
import { ParsedDID, extractIdentifier, extractMethod, formatDid, parse } from "./parse.cjs";
|
|
6
|
+
import { isKnownDid } from "./validate.cjs";
|
|
7
|
+
import { expandShortForm, toShortForm } from "./short-form.cjs";
|
|
8
|
+
import { NameHierarchy, ResolveRoute, getResolveRoute, isGlobalName, isLocalName, parseNameHierarchy } from "./name.cjs";
|
|
9
|
+
import { InMemoryNameRegistry, NameRegistry } from "./name-registry.cjs";
|
|
10
|
+
import { DIDNameResolver, DIDNameResolverOptions, ResolveTraceResult } from "./name-resolver.cjs";
|
|
3
11
|
import { types } from "@ocap/mcrypto";
|
|
4
12
|
import { BytesType, toAddress, toDid } from "@ocap/util";
|
|
5
13
|
|
|
@@ -58,4 +66,4 @@ declare const isFromPublicKey: (did: string, pk: BytesType) => boolean;
|
|
|
58
66
|
*/
|
|
59
67
|
declare const isValid: (did: string) => boolean;
|
|
60
68
|
//#endregion
|
|
61
|
-
export { type DIDType, type DIDTypeArg, type DIDTypeShortcut, type DIDTypeStr, DID_PREFIX, DID_TYPE_ARCBLOCK, DID_TYPE_ETHEREUM, DID_TYPE_PASSKEY, DidType, type RequiredDIDType, fromHash, fromPublicKey, fromPublicKeyHash, fromSecretKey, fromTypeInfo, isEthereumDid, isEthereumType, isFromPublicKey, isValid, toAddress, toDid, toStrictHex, toTypeInfo, toTypeInfoStr, types };
|
|
69
|
+
export { ALIAS_METHODS, ALL_METHODS, type AliasMethod, CRYPTO_METHODS, type CryptoMethod, DEFAULT_METHOD, type DIDMethod, DIDNameResolver, type DIDNameResolverOptions, type DIDType, type DIDTypeArg, type DIDTypeShortcut, type DIDTypeStr, DID_PREFIX, DID_TYPE_ARCBLOCK, DID_TYPE_ETHEREUM, DID_TYPE_PASSKEY, DidType, InMemoryNameRegistry, type NameHierarchy, type NameRegistry, type ParsedDID, type RequiredDIDType, type ResolveRoute, type ResolveTraceResult, expandShortForm, extractIdentifier, extractMethod, formatDid, fromHash, fromPublicKey, fromPublicKeyHash, fromSecretKey, fromTypeInfo, getEntityId, getResolveRoute, isAliasMethod, isCryptoMethod, isEthereumDid, isEthereumType, isFromPublicKey, isGlobalName, isKnownDid, isKnownMethod, isLocalName, isSameEntity, isValid, parse, parseNameHierarchy, toAddress, toDid, toShortForm, toStrictHex, toTypeInfo, toTypeInfoStr, types };
|
package/lib/method.cjs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/method.ts
|
|
3
|
+
/** Crypto methods: share unified identifier encoding (base58 checksum / ethereum / base16) */
|
|
4
|
+
const CRYPTO_METHODS = Object.freeze([
|
|
5
|
+
"abt",
|
|
6
|
+
"afs",
|
|
7
|
+
"aos",
|
|
8
|
+
"spaces"
|
|
9
|
+
]);
|
|
10
|
+
/** Alias methods: human-readable identifiers */
|
|
11
|
+
const ALIAS_METHODS = Object.freeze(["name"]);
|
|
12
|
+
/** All supported methods */
|
|
13
|
+
const ALL_METHODS = Object.freeze([...CRYPTO_METHODS, ...ALIAS_METHODS]);
|
|
14
|
+
/** Default method when none is specified */
|
|
15
|
+
const DEFAULT_METHOD = "abt";
|
|
16
|
+
/** Check if a value is a known crypto method */
|
|
17
|
+
function isCryptoMethod(method) {
|
|
18
|
+
if (typeof method !== "string") return false;
|
|
19
|
+
return CRYPTO_METHODS.includes(method);
|
|
20
|
+
}
|
|
21
|
+
/** Check if a value is a known alias method */
|
|
22
|
+
function isAliasMethod(method) {
|
|
23
|
+
if (typeof method !== "string") return false;
|
|
24
|
+
return ALIAS_METHODS.includes(method);
|
|
25
|
+
}
|
|
26
|
+
/** Check if a value is any known method */
|
|
27
|
+
function isKnownMethod(method) {
|
|
28
|
+
if (typeof method !== "string") return false;
|
|
29
|
+
return ALL_METHODS.includes(method);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
exports.ALIAS_METHODS = ALIAS_METHODS;
|
|
34
|
+
exports.ALL_METHODS = ALL_METHODS;
|
|
35
|
+
exports.CRYPTO_METHODS = CRYPTO_METHODS;
|
|
36
|
+
exports.DEFAULT_METHOD = DEFAULT_METHOD;
|
|
37
|
+
exports.isAliasMethod = isAliasMethod;
|
|
38
|
+
exports.isCryptoMethod = isCryptoMethod;
|
|
39
|
+
exports.isKnownMethod = isKnownMethod;
|
package/lib/method.d.cts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/method.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* DID Method Registry
|
|
4
|
+
*
|
|
5
|
+
* Defines supported DID methods, their categories, and helper functions.
|
|
6
|
+
* Crypto methods (abt, afs, aos, spaces) share a unified identifier encoding.
|
|
7
|
+
* Alias methods (name) use human-readable identifiers with DNS-style hierarchy.
|
|
8
|
+
*/
|
|
9
|
+
/** Crypto methods share unified identifier encoding */
|
|
10
|
+
type CryptoMethod = 'abt' | 'afs' | 'aos' | 'spaces';
|
|
11
|
+
/** Alias methods use human-readable identifiers */
|
|
12
|
+
type AliasMethod = 'name';
|
|
13
|
+
/** All supported DID methods */
|
|
14
|
+
type DIDMethod = CryptoMethod | AliasMethod;
|
|
15
|
+
/** Crypto methods: share unified identifier encoding (base58 checksum / ethereum / base16) */
|
|
16
|
+
declare const CRYPTO_METHODS: readonly CryptoMethod[];
|
|
17
|
+
/** Alias methods: human-readable identifiers */
|
|
18
|
+
declare const ALIAS_METHODS: readonly AliasMethod[];
|
|
19
|
+
/** All supported methods */
|
|
20
|
+
declare const ALL_METHODS: readonly DIDMethod[];
|
|
21
|
+
/** Default method when none is specified */
|
|
22
|
+
declare const DEFAULT_METHOD: CryptoMethod;
|
|
23
|
+
/** Check if a value is a known crypto method */
|
|
24
|
+
declare function isCryptoMethod(method: unknown): method is CryptoMethod;
|
|
25
|
+
/** Check if a value is a known alias method */
|
|
26
|
+
declare function isAliasMethod(method: unknown): method is AliasMethod;
|
|
27
|
+
/** Check if a value is any known method */
|
|
28
|
+
declare function isKnownMethod(method: unknown): method is DIDMethod;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { ALIAS_METHODS, ALL_METHODS, AliasMethod, CRYPTO_METHODS, CryptoMethod, DEFAULT_METHOD, DIDMethod, isAliasMethod, isCryptoMethod, isKnownMethod };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/name-registry.ts
|
|
3
|
+
const MAX_NAME_LENGTH = 1024;
|
|
4
|
+
/**
|
|
5
|
+
* In-memory name registry implementation.
|
|
6
|
+
* Uses upsert semantics: re-registering a name overwrites the previous value.
|
|
7
|
+
*/
|
|
8
|
+
var InMemoryNameRegistry = class {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.store = /* @__PURE__ */ new Map();
|
|
11
|
+
}
|
|
12
|
+
resolve(name) {
|
|
13
|
+
return this.store.get(name) ?? null;
|
|
14
|
+
}
|
|
15
|
+
register(name, did) {
|
|
16
|
+
if (!name || typeof name !== "string") throw new Error("Name must be a non-empty string");
|
|
17
|
+
if (name.length > MAX_NAME_LENGTH) throw new Error("Name exceeds maximum length");
|
|
18
|
+
if (name.startsWith(".") || name.endsWith(".")) throw new Error("Name cannot start or end with a dot");
|
|
19
|
+
if (!did || typeof did !== "string") throw new Error("DID must be a non-empty string");
|
|
20
|
+
if (!did.startsWith("did:")) throw new Error("DID must have a valid did: prefix");
|
|
21
|
+
this.store.set(name, did);
|
|
22
|
+
}
|
|
23
|
+
unregister(name) {
|
|
24
|
+
this.store.delete(name);
|
|
25
|
+
}
|
|
26
|
+
has(name) {
|
|
27
|
+
return this.store.has(name);
|
|
28
|
+
}
|
|
29
|
+
list() {
|
|
30
|
+
return [...this.store.keys()];
|
|
31
|
+
}
|
|
32
|
+
clear() {
|
|
33
|
+
this.store.clear();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.InMemoryNameRegistry = InMemoryNameRegistry;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/name-registry.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* NameRegistry Interface and InMemoryNameRegistry Implementation
|
|
4
|
+
*
|
|
5
|
+
* Provides a synchronous interface for name→DID registration and lookup.
|
|
6
|
+
* InMemoryNameRegistry is a Map-based implementation for testing.
|
|
7
|
+
*/
|
|
8
|
+
/** Name registry interface (synchronous) */
|
|
9
|
+
interface NameRegistry {
|
|
10
|
+
resolve(name: string): string | null;
|
|
11
|
+
register(name: string, did: string): void;
|
|
12
|
+
unregister(name: string): void;
|
|
13
|
+
has(name: string): boolean;
|
|
14
|
+
list(): string[];
|
|
15
|
+
clear(): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* In-memory name registry implementation.
|
|
19
|
+
* Uses upsert semantics: re-registering a name overwrites the previous value.
|
|
20
|
+
*/
|
|
21
|
+
declare class InMemoryNameRegistry implements NameRegistry {
|
|
22
|
+
private store;
|
|
23
|
+
resolve(name: string): string | null;
|
|
24
|
+
register(name: string, did: string): void;
|
|
25
|
+
unregister(name: string): void;
|
|
26
|
+
has(name: string): boolean;
|
|
27
|
+
list(): string[];
|
|
28
|
+
clear(): void;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { InMemoryNameRegistry, NameRegistry };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const require_name = require('./name.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/name-resolver.ts
|
|
4
|
+
/**
|
|
5
|
+
* DID Name Resolver
|
|
6
|
+
*
|
|
7
|
+
* Orchestrates name resolution across different registries (global, local).
|
|
8
|
+
* Supports delegation chains with cycle detection and depth limiting.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_MAX_DEPTH = 10;
|
|
11
|
+
/**
|
|
12
|
+
* DID Name Resolver.
|
|
13
|
+
*
|
|
14
|
+
* Routes name resolution to the appropriate registry based on the name structure:
|
|
15
|
+
* - Global names (single segment) → global registry
|
|
16
|
+
* - .local names → local registry
|
|
17
|
+
* - Delegated names (multi-segment) → resolve TLD first, then delegate
|
|
18
|
+
*/
|
|
19
|
+
var DIDNameResolver = class {
|
|
20
|
+
constructor(opts = {}) {
|
|
21
|
+
this.global = opts.global;
|
|
22
|
+
this.local = opts.local;
|
|
23
|
+
this.maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a name to a DID.
|
|
27
|
+
*
|
|
28
|
+
* @param name - The name to resolve (can be prefixed with 'did:name:')
|
|
29
|
+
* @returns The resolved DID, or null if not found
|
|
30
|
+
*/
|
|
31
|
+
async resolve(name) {
|
|
32
|
+
return (await this.resolveWithTrace(name)).result;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a name with full trace for debugging.
|
|
36
|
+
*/
|
|
37
|
+
async resolveWithTrace(name) {
|
|
38
|
+
if (!name || typeof name !== "string") throw new Error("Name must be a non-empty string");
|
|
39
|
+
const cleanName = name.replace(/^did:name:/, "");
|
|
40
|
+
if (!cleanName) throw new Error("Name cannot be empty after stripping prefix");
|
|
41
|
+
const visited = /* @__PURE__ */ new Set();
|
|
42
|
+
return this._resolve(cleanName, visited, [], 0);
|
|
43
|
+
}
|
|
44
|
+
async _resolve(name, visited, trace, depth) {
|
|
45
|
+
if (visited.has(name)) {
|
|
46
|
+
trace.push(`cycle_detected:${name}`);
|
|
47
|
+
return {
|
|
48
|
+
result: null,
|
|
49
|
+
trace
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (depth >= this.maxDepth) {
|
|
53
|
+
trace.push(`max_depth:${name}`);
|
|
54
|
+
return {
|
|
55
|
+
result: null,
|
|
56
|
+
trace
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
visited.add(name);
|
|
60
|
+
const route = require_name.getResolveRoute(name);
|
|
61
|
+
let resolved = null;
|
|
62
|
+
if (route.type === "local") {
|
|
63
|
+
trace.push(`local:${name}`);
|
|
64
|
+
if (this.local) resolved = this.local.resolve(route.name);
|
|
65
|
+
} else if (route.type === "global") {
|
|
66
|
+
trace.push(`global:${name}`);
|
|
67
|
+
if (this.global) resolved = this.global.resolve(route.name);
|
|
68
|
+
} else {
|
|
69
|
+
trace.push(`delegated:${name}`);
|
|
70
|
+
if (this.global) {
|
|
71
|
+
resolved = this.global.resolve(name);
|
|
72
|
+
if (!resolved) {
|
|
73
|
+
const tldResult = this.global.resolve(route.tld);
|
|
74
|
+
if (tldResult) resolved = tldResult;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!resolved) {
|
|
79
|
+
trace.push(`not_found:${name}`);
|
|
80
|
+
return {
|
|
81
|
+
result: null,
|
|
82
|
+
trace
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (resolved.startsWith("did:name:")) {
|
|
86
|
+
const nextName = resolved.replace(/^did:name:/, "");
|
|
87
|
+
return this._resolve(nextName, visited, trace, depth + 1);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
result: resolved,
|
|
91
|
+
trace
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
exports.DIDNameResolver = DIDNameResolver;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NameRegistry } from "./name-registry.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/name-resolver.d.ts
|
|
4
|
+
|
|
5
|
+
interface DIDNameResolverOptions {
|
|
6
|
+
global?: NameRegistry;
|
|
7
|
+
local?: NameRegistry;
|
|
8
|
+
maxDepth?: number;
|
|
9
|
+
}
|
|
10
|
+
interface ResolveTraceResult {
|
|
11
|
+
result: string | null;
|
|
12
|
+
trace: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* DID Name Resolver.
|
|
16
|
+
*
|
|
17
|
+
* Routes name resolution to the appropriate registry based on the name structure:
|
|
18
|
+
* - Global names (single segment) → global registry
|
|
19
|
+
* - .local names → local registry
|
|
20
|
+
* - Delegated names (multi-segment) → resolve TLD first, then delegate
|
|
21
|
+
*/
|
|
22
|
+
declare class DIDNameResolver {
|
|
23
|
+
private global?;
|
|
24
|
+
private local?;
|
|
25
|
+
private maxDepth;
|
|
26
|
+
constructor(opts?: DIDNameResolverOptions);
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a name to a DID.
|
|
29
|
+
*
|
|
30
|
+
* @param name - The name to resolve (can be prefixed with 'did:name:')
|
|
31
|
+
* @returns The resolved DID, or null if not found
|
|
32
|
+
*/
|
|
33
|
+
resolve(name: string): Promise<string | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a name with full trace for debugging.
|
|
36
|
+
*/
|
|
37
|
+
resolveWithTrace(name: string): Promise<ResolveTraceResult>;
|
|
38
|
+
private _resolve;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { DIDNameResolver, DIDNameResolverOptions, ResolveTraceResult };
|
package/lib/name.cjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/name.ts
|
|
3
|
+
/**
|
|
4
|
+
* Parse a name identifier into its hierarchy components.
|
|
5
|
+
*
|
|
6
|
+
* @param name - The name to parse (e.g. 'robertmao', 'bob.myorg', 'anything.local')
|
|
7
|
+
* @returns Parsed hierarchy
|
|
8
|
+
* @throws If name is invalid
|
|
9
|
+
*/
|
|
10
|
+
function parseNameHierarchy(name) {
|
|
11
|
+
if (typeof name !== "string") throw new Error("Name must be a string");
|
|
12
|
+
if (!name) throw new Error("Name cannot be empty");
|
|
13
|
+
if (name.startsWith(".")) throw new Error("Name cannot start with a dot");
|
|
14
|
+
if (name.endsWith(".")) throw new Error("Name cannot end with a dot");
|
|
15
|
+
if (name.includes("..")) throw new Error("Name cannot contain consecutive dots");
|
|
16
|
+
const segments = name.split(".");
|
|
17
|
+
if (segments.some((s) => !s)) throw new Error("Name contains empty segments");
|
|
18
|
+
const tld = segments[segments.length - 1];
|
|
19
|
+
const isLocal = tld === "local";
|
|
20
|
+
const isGlobal = segments.length === 1 && !isLocal;
|
|
21
|
+
const depth = segments.length;
|
|
22
|
+
return {
|
|
23
|
+
segments: [...segments],
|
|
24
|
+
tld,
|
|
25
|
+
isLocal,
|
|
26
|
+
isGlobal,
|
|
27
|
+
depth
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Determine the resolve route for a name.
|
|
32
|
+
*
|
|
33
|
+
* - Single segment (e.g. 'robertmao') → global route
|
|
34
|
+
* - Ends with '.local' → local route
|
|
35
|
+
* - Multi-segment (e.g. 'bob.myorg') → delegated route
|
|
36
|
+
*
|
|
37
|
+
* @param name - The name to route
|
|
38
|
+
* @returns The resolve route
|
|
39
|
+
* @throws If name is invalid
|
|
40
|
+
*/
|
|
41
|
+
function getResolveRoute(name) {
|
|
42
|
+
const hierarchy = parseNameHierarchy(name);
|
|
43
|
+
if (hierarchy.isLocal) return {
|
|
44
|
+
type: "local",
|
|
45
|
+
name: hierarchy.segments.slice(0, -1).join(".")
|
|
46
|
+
};
|
|
47
|
+
if (hierarchy.isGlobal) return {
|
|
48
|
+
type: "global",
|
|
49
|
+
name
|
|
50
|
+
};
|
|
51
|
+
const subName = hierarchy.segments.slice(0, -1).join(".");
|
|
52
|
+
return {
|
|
53
|
+
type: "delegated",
|
|
54
|
+
tld: hierarchy.tld,
|
|
55
|
+
subName
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if a name is a .local name.
|
|
60
|
+
*/
|
|
61
|
+
function isLocalName(name) {
|
|
62
|
+
if (!name || !name.includes(".")) return false;
|
|
63
|
+
return name.endsWith(".local");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if a name is a global name (single segment, not .local).
|
|
67
|
+
*/
|
|
68
|
+
function isGlobalName(name) {
|
|
69
|
+
if (!name) return false;
|
|
70
|
+
return !name.includes(".");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
exports.getResolveRoute = getResolveRoute;
|
|
75
|
+
exports.isGlobalName = isGlobalName;
|
|
76
|
+
exports.isLocalName = isLocalName;
|
|
77
|
+
exports.parseNameHierarchy = parseNameHierarchy;
|