@arcblock/did 1.29.17 → 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.
@@ -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/esm/entity.mjs ADDED
@@ -0,0 +1,39 @@
1
+ import { extractIdentifier } from "./parse.mjs";
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 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 = extractIdentifier(a);
33
+ const idB = extractIdentifier(b);
34
+ if (!idA || !idB) return false;
35
+ return idA.toLowerCase() === idB.toLowerCase();
36
+ }
37
+
38
+ //#endregion
39
+ export { getEntityId, isSameEntity };
package/esm/index.d.mts CHANGED
@@ -1,5 +1,13 @@
1
+ import { getEntityId, isSameEntity } from "./entity.mjs";
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.mjs";
2
3
  import { DID_PREFIX, toStrictHex } from "./util.mjs";
4
+ import { ALIAS_METHODS, ALL_METHODS, AliasMethod, CRYPTO_METHODS, CryptoMethod, DEFAULT_METHOD, DIDMethod, isAliasMethod, isCryptoMethod, isKnownMethod } from "./method.mjs";
5
+ import { ParsedDID, extractIdentifier, extractMethod, formatDid, parse } from "./parse.mjs";
6
+ import { isKnownDid } from "./validate.mjs";
7
+ import { expandShortForm, toShortForm } from "./short-form.mjs";
8
+ import { NameHierarchy, ResolveRoute, getResolveRoute, isGlobalName, isLocalName, parseNameHierarchy } from "./name.mjs";
9
+ import { InMemoryNameRegistry, NameRegistry } from "./name-registry.mjs";
10
+ import { DIDNameResolver, DIDNameResolverOptions, ResolveTraceResult } from "./name-resolver.mjs";
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/esm/index.mjs CHANGED
@@ -1,5 +1,13 @@
1
+ import { ALIAS_METHODS, ALL_METHODS, CRYPTO_METHODS, DEFAULT_METHOD, isAliasMethod, isCryptoMethod, isKnownMethod } from "./method.mjs";
2
+ import { extractIdentifier, extractMethod, formatDid, parse } from "./parse.mjs";
3
+ import { getEntityId, isSameEntity } from "./entity.mjs";
1
4
  import { DID_PREFIX, toBytes, toStrictHex } from "./util.mjs";
2
5
  import { DID_TYPE_ARCBLOCK, DID_TYPE_ETHEREUM, DID_TYPE_PASSKEY, DidType, fromTypeInfo, isEthereumDid, isEthereumType, toChecksumAddress, toTypeInfo, toTypeInfoStr } from "./type.mjs";
6
+ import { isKnownDid } from "./validate.mjs";
7
+ import { expandShortForm, toShortForm } from "./short-form.mjs";
8
+ import { getResolveRoute, isGlobalName, isLocalName, parseNameHierarchy } from "./name.mjs";
9
+ import { InMemoryNameRegistry } from "./name-registry.mjs";
10
+ import { DIDNameResolver } from "./name-resolver.mjs";
3
11
  import { getHasher, getSigner, types } from "@ocap/mcrypto";
4
12
  import { stripHexPrefix, toAddress, toBase58, toDid } from "@ocap/util";
5
13
 
@@ -69,7 +77,7 @@ const fromHash = (hash, role = types.RoleType.ROLE_ACCOUNT) => {
69
77
  */
70
78
  const isFromPublicKey = (did, pk) => {
71
79
  if (isValid(did) === false) return false;
72
- return fromPublicKey(pk, toTypeInfo(did)) === did.replace(DID_PREFIX, "");
80
+ return fromPublicKey(pk, toTypeInfo(did)) === toAddress(did);
73
81
  };
74
82
  /**
75
83
  * Check if a DID string is valid
@@ -92,4 +100,4 @@ const isValid = (did) => {
92
100
  };
93
101
 
94
102
  //#endregion
95
- export { DID_PREFIX, DID_TYPE_ARCBLOCK, DID_TYPE_ETHEREUM, DID_TYPE_PASSKEY, DidType, fromHash, fromPublicKey, fromPublicKeyHash, fromSecretKey, fromTypeInfo, isEthereumDid, isEthereumType, isFromPublicKey, isValid, toAddress, toDid, toStrictHex, toTypeInfo, toTypeInfoStr, types };
103
+ export { ALIAS_METHODS, ALL_METHODS, CRYPTO_METHODS, DEFAULT_METHOD, DIDNameResolver, DID_PREFIX, DID_TYPE_ARCBLOCK, DID_TYPE_ETHEREUM, DID_TYPE_PASSKEY, DidType, InMemoryNameRegistry, 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 };
@@ -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 };
package/esm/method.mjs ADDED
@@ -0,0 +1,32 @@
1
+ //#region src/method.ts
2
+ /** Crypto methods: share unified identifier encoding (base58 checksum / ethereum / base16) */
3
+ const CRYPTO_METHODS = Object.freeze([
4
+ "abt",
5
+ "afs",
6
+ "aos",
7
+ "spaces"
8
+ ]);
9
+ /** Alias methods: human-readable identifiers */
10
+ const ALIAS_METHODS = Object.freeze(["name"]);
11
+ /** All supported methods */
12
+ const ALL_METHODS = Object.freeze([...CRYPTO_METHODS, ...ALIAS_METHODS]);
13
+ /** Default method when none is specified */
14
+ const DEFAULT_METHOD = "abt";
15
+ /** Check if a value is a known crypto method */
16
+ function isCryptoMethod(method) {
17
+ if (typeof method !== "string") return false;
18
+ return CRYPTO_METHODS.includes(method);
19
+ }
20
+ /** Check if a value is a known alias method */
21
+ function isAliasMethod(method) {
22
+ if (typeof method !== "string") return false;
23
+ return ALIAS_METHODS.includes(method);
24
+ }
25
+ /** Check if a value is any known method */
26
+ function isKnownMethod(method) {
27
+ if (typeof method !== "string") return false;
28
+ return ALL_METHODS.includes(method);
29
+ }
30
+
31
+ //#endregion
32
+ export { ALIAS_METHODS, ALL_METHODS, CRYPTO_METHODS, DEFAULT_METHOD, isAliasMethod, isCryptoMethod, isKnownMethod };
@@ -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,37 @@
1
+ //#region src/name-registry.ts
2
+ const MAX_NAME_LENGTH = 1024;
3
+ /**
4
+ * In-memory name registry implementation.
5
+ * Uses upsert semantics: re-registering a name overwrites the previous value.
6
+ */
7
+ var InMemoryNameRegistry = class {
8
+ constructor() {
9
+ this.store = /* @__PURE__ */ new Map();
10
+ }
11
+ resolve(name) {
12
+ return this.store.get(name) ?? null;
13
+ }
14
+ register(name, did) {
15
+ if (!name || typeof name !== "string") throw new Error("Name must be a non-empty string");
16
+ if (name.length > MAX_NAME_LENGTH) throw new Error("Name exceeds maximum length");
17
+ if (name.startsWith(".") || name.endsWith(".")) throw new Error("Name cannot start or end with a dot");
18
+ if (!did || typeof did !== "string") throw new Error("DID must be a non-empty string");
19
+ if (!did.startsWith("did:")) throw new Error("DID must have a valid did: prefix");
20
+ this.store.set(name, did);
21
+ }
22
+ unregister(name) {
23
+ this.store.delete(name);
24
+ }
25
+ has(name) {
26
+ return this.store.has(name);
27
+ }
28
+ list() {
29
+ return [...this.store.keys()];
30
+ }
31
+ clear() {
32
+ this.store.clear();
33
+ }
34
+ };
35
+
36
+ //#endregion
37
+ export { InMemoryNameRegistry };
@@ -0,0 +1,41 @@
1
+ import { NameRegistry } from "./name-registry.mjs";
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 };
@@ -0,0 +1,97 @@
1
+ import { getResolveRoute } from "./name.mjs";
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 = 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
+ export { DIDNameResolver };
package/esm/name.d.mts ADDED
@@ -0,0 +1,62 @@
1
+ //#region src/name.d.ts
2
+ /**
3
+ * DID Name Hierarchy Parser
4
+ *
5
+ * DNS-style right-to-left name resolution for did:name identifiers.
6
+ * Supports global names, delegated sub-names, and .local domains.
7
+ */
8
+ /** Parsed name hierarchy */
9
+ interface NameHierarchy {
10
+ /** Name segments split by '.' */
11
+ segments: string[];
12
+ /** Top-level domain (rightmost segment) */
13
+ tld: string;
14
+ /** Whether this is a .local name */
15
+ isLocal: boolean;
16
+ /** Whether this is a global name (single segment, not .local) */
17
+ isGlobal: boolean;
18
+ /** Number of segments */
19
+ depth: number;
20
+ }
21
+ /** Resolve route types */
22
+ type ResolveRoute = {
23
+ type: 'global';
24
+ name: string;
25
+ } | {
26
+ type: 'local';
27
+ name: string;
28
+ } | {
29
+ type: 'delegated';
30
+ tld: string;
31
+ subName: string;
32
+ };
33
+ /**
34
+ * Parse a name identifier into its hierarchy components.
35
+ *
36
+ * @param name - The name to parse (e.g. 'robertmao', 'bob.myorg', 'anything.local')
37
+ * @returns Parsed hierarchy
38
+ * @throws If name is invalid
39
+ */
40
+ declare function parseNameHierarchy(name: unknown): NameHierarchy;
41
+ /**
42
+ * Determine the resolve route for a name.
43
+ *
44
+ * - Single segment (e.g. 'robertmao') → global route
45
+ * - Ends with '.local' → local route
46
+ * - Multi-segment (e.g. 'bob.myorg') → delegated route
47
+ *
48
+ * @param name - The name to route
49
+ * @returns The resolve route
50
+ * @throws If name is invalid
51
+ */
52
+ declare function getResolveRoute(name: string): ResolveRoute;
53
+ /**
54
+ * Check if a name is a .local name.
55
+ */
56
+ declare function isLocalName(name: string): boolean;
57
+ /**
58
+ * Check if a name is a global name (single segment, not .local).
59
+ */
60
+ declare function isGlobalName(name: string): boolean;
61
+ //#endregion
62
+ export { NameHierarchy, ResolveRoute, getResolveRoute, isGlobalName, isLocalName, parseNameHierarchy };
package/esm/name.mjs ADDED
@@ -0,0 +1,73 @@
1
+ //#region src/name.ts
2
+ /**
3
+ * Parse a name identifier into its hierarchy components.
4
+ *
5
+ * @param name - The name to parse (e.g. 'robertmao', 'bob.myorg', 'anything.local')
6
+ * @returns Parsed hierarchy
7
+ * @throws If name is invalid
8
+ */
9
+ function parseNameHierarchy(name) {
10
+ if (typeof name !== "string") throw new Error("Name must be a string");
11
+ if (!name) throw new Error("Name cannot be empty");
12
+ if (name.startsWith(".")) throw new Error("Name cannot start with a dot");
13
+ if (name.endsWith(".")) throw new Error("Name cannot end with a dot");
14
+ if (name.includes("..")) throw new Error("Name cannot contain consecutive dots");
15
+ const segments = name.split(".");
16
+ if (segments.some((s) => !s)) throw new Error("Name contains empty segments");
17
+ const tld = segments[segments.length - 1];
18
+ const isLocal = tld === "local";
19
+ const isGlobal = segments.length === 1 && !isLocal;
20
+ const depth = segments.length;
21
+ return {
22
+ segments: [...segments],
23
+ tld,
24
+ isLocal,
25
+ isGlobal,
26
+ depth
27
+ };
28
+ }
29
+ /**
30
+ * Determine the resolve route for a name.
31
+ *
32
+ * - Single segment (e.g. 'robertmao') → global route
33
+ * - Ends with '.local' → local route
34
+ * - Multi-segment (e.g. 'bob.myorg') → delegated route
35
+ *
36
+ * @param name - The name to route
37
+ * @returns The resolve route
38
+ * @throws If name is invalid
39
+ */
40
+ function getResolveRoute(name) {
41
+ const hierarchy = parseNameHierarchy(name);
42
+ if (hierarchy.isLocal) return {
43
+ type: "local",
44
+ name: hierarchy.segments.slice(0, -1).join(".")
45
+ };
46
+ if (hierarchy.isGlobal) return {
47
+ type: "global",
48
+ name
49
+ };
50
+ const subName = hierarchy.segments.slice(0, -1).join(".");
51
+ return {
52
+ type: "delegated",
53
+ tld: hierarchy.tld,
54
+ subName
55
+ };
56
+ }
57
+ /**
58
+ * Check if a name is a .local name.
59
+ */
60
+ function isLocalName(name) {
61
+ if (!name || !name.includes(".")) return false;
62
+ return name.endsWith(".local");
63
+ }
64
+ /**
65
+ * Check if a name is a global name (single segment, not .local).
66
+ */
67
+ function isGlobalName(name) {
68
+ if (!name) return false;
69
+ return !name.includes(".");
70
+ }
71
+
72
+ //#endregion
73
+ export { getResolveRoute, isGlobalName, isLocalName, parseNameHierarchy };
@@ -0,0 +1,50 @@
1
+ import { DIDMethod } from "./method.mjs";
2
+
3
+ //#region src/parse.d.ts
4
+
5
+ /** Parsed DID structure */
6
+ interface ParsedDID {
7
+ /** The method component (e.g. 'abt', 'afs', 'name') */
8
+ method: DIDMethod;
9
+ /** The identifier component (everything after did:{method}:) */
10
+ identifier: string;
11
+ /** The full DID string */
12
+ full: string;
13
+ }
14
+ /**
15
+ * Parse a full DID string into its components.
16
+ *
17
+ * @param did - Full DID string (e.g. 'did:abt:z1abc...')
18
+ * @returns Parsed DID object
19
+ * @throws If the input is not a valid DID string
20
+ */
21
+ declare function parse(did: unknown): ParsedDID;
22
+ /**
23
+ * Format an identifier and method into a full DID string.
24
+ *
25
+ * Handles idempotency: if the identifier already has a `did:{method}:` prefix
26
+ * matching the target method, returns it as-is.
27
+ *
28
+ * @param identifier - The identifier (address or name)
29
+ * @param method - The DID method (default: 'abt')
30
+ * @returns Full DID string
31
+ * @throws If the method is unknown or identifier is empty
32
+ */
33
+ declare function formatDid(identifier: string, method?: DIDMethod): string;
34
+ /**
35
+ * Extract the method from a DID string.
36
+ *
37
+ * @param did - A DID string or bare address
38
+ * @returns The method string, or null if no prefix found
39
+ */
40
+ declare function extractMethod(did: unknown): DIDMethod | null;
41
+ /**
42
+ * Extract the identifier from a DID string, stripping the `did:{method}:` prefix.
43
+ * If the input has no prefix, returns it as-is.
44
+ *
45
+ * @param did - A DID string or bare address
46
+ * @returns The identifier portion
47
+ */
48
+ declare function extractIdentifier(did: unknown): string;
49
+ //#endregion
50
+ export { ParsedDID, extractIdentifier, extractMethod, formatDid, parse };
package/esm/parse.mjs ADDED
@@ -0,0 +1,88 @@
1
+ import { DEFAULT_METHOD, isKnownMethod } from "./method.mjs";
2
+
3
+ //#region src/parse.ts
4
+ /**
5
+ * Universal DID Parser
6
+ *
7
+ * Parses, formats, and extracts components from DID strings.
8
+ * Supports all registered methods (abt, afs, aos, spaces, name).
9
+ */
10
+ /** DID format regex: did:{method}:{identifier} where method is [a-z0-9]+ */
11
+ const DID_REGEX = /^did:([a-z0-9]+):(.+)$/;
12
+ /**
13
+ * Parse a full DID string into its components.
14
+ *
15
+ * @param did - Full DID string (e.g. 'did:abt:z1abc...')
16
+ * @returns Parsed DID object
17
+ * @throws If the input is not a valid DID string
18
+ */
19
+ function parse(did) {
20
+ if (typeof did !== "string") throw new Error("DID must be a string");
21
+ if (!did) throw new Error("DID string cannot be empty");
22
+ const match = did.match(DID_REGEX);
23
+ if (!match) throw new Error("Invalid DID format");
24
+ const [, method, identifier] = match;
25
+ if (!method) throw new Error("Invalid DID format");
26
+ if (!identifier) throw new Error("Invalid DID format");
27
+ if (!isKnownMethod(method)) throw new Error(`Unknown DID method: ${method}`);
28
+ return {
29
+ method,
30
+ identifier,
31
+ full: did
32
+ };
33
+ }
34
+ /**
35
+ * Format an identifier and method into a full DID string.
36
+ *
37
+ * Handles idempotency: if the identifier already has a `did:{method}:` prefix
38
+ * matching the target method, returns it as-is.
39
+ *
40
+ * @param identifier - The identifier (address or name)
41
+ * @param method - The DID method (default: 'abt')
42
+ * @returns Full DID string
43
+ * @throws If the method is unknown or identifier is empty
44
+ */
45
+ function formatDid(identifier, method = DEFAULT_METHOD) {
46
+ if (!identifier) throw new Error("Identifier cannot be empty");
47
+ if (!isKnownMethod(method)) throw new Error(`Unknown DID method: ${method}`);
48
+ const prefix = `did:${method}:`;
49
+ if (identifier.startsWith(prefix)) return identifier;
50
+ if (identifier.startsWith("did:")) {
51
+ const match = identifier.match(DID_REGEX);
52
+ if (match) {
53
+ if (match[1] === method) return identifier;
54
+ return `did:${method}:${match[2]}`;
55
+ }
56
+ }
57
+ return `did:${method}:${identifier}`;
58
+ }
59
+ /**
60
+ * Extract the method from a DID string.
61
+ *
62
+ * @param did - A DID string or bare address
63
+ * @returns The method string, or null if no prefix found
64
+ */
65
+ function extractMethod(did) {
66
+ if (typeof did !== "string" || !did) return null;
67
+ const match = did.match(DID_REGEX);
68
+ if (!match) return null;
69
+ const method = match[1];
70
+ if (isKnownMethod(method)) return method;
71
+ return null;
72
+ }
73
+ /**
74
+ * Extract the identifier from a DID string, stripping the `did:{method}:` prefix.
75
+ * If the input has no prefix, returns it as-is.
76
+ *
77
+ * @param did - A DID string or bare address
78
+ * @returns The identifier portion
79
+ */
80
+ function extractIdentifier(did) {
81
+ if (typeof did !== "string" || !did) return "";
82
+ const match = did.match(/^did:[a-z0-9]+:(.+)$/);
83
+ if (match) return match[1];
84
+ return did;
85
+ }
86
+
87
+ //#endregion
88
+ export { extractIdentifier, extractMethod, formatDid, parse };