@arcblock/did
Version:
Javascript lib to work with ArcBlock DID
97 lines (95 loc) • 2.62 kB
JavaScript
import { getResolveRoute } from "./name.mjs";
//#region src/name-resolver.ts
/**
* DID Name Resolver
*
* Orchestrates name resolution across different registries (global, local).
* Supports delegation chains with cycle detection and depth limiting.
*/
const DEFAULT_MAX_DEPTH = 10;
/**
* DID Name Resolver.
*
* Routes name resolution to the appropriate registry based on the name structure:
* - Global names (single segment) → global registry
* - .local names → local registry
* - Delegated names (multi-segment) → resolve TLD first, then delegate
*/
var DIDNameResolver = class {
constructor(opts = {}) {
this.global = opts.global;
this.local = opts.local;
this.maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
}
/**
* Resolve a name to a DID.
*
* @param name - The name to resolve (can be prefixed with 'did:name:')
* @returns The resolved DID, or null if not found
*/
async resolve(name) {
return (await this.resolveWithTrace(name)).result;
}
/**
* Resolve a name with full trace for debugging.
*/
async resolveWithTrace(name) {
if (!name || typeof name !== "string") throw new Error("Name must be a non-empty string");
const cleanName = name.replace(/^did:name:/, "");
if (!cleanName) throw new Error("Name cannot be empty after stripping prefix");
const visited = /* @__PURE__ */ new Set();
return this._resolve(cleanName, visited, [], 0);
}
async _resolve(name, visited, trace, depth) {
if (visited.has(name)) {
trace.push(`cycle_detected:${name}`);
return {
result: null,
trace
};
}
if (depth >= this.maxDepth) {
trace.push(`max_depth:${name}`);
return {
result: null,
trace
};
}
visited.add(name);
const route = getResolveRoute(name);
let resolved = null;
if (route.type === "local") {
trace.push(`local:${name}`);
if (this.local) resolved = this.local.resolve(route.name);
} else if (route.type === "global") {
trace.push(`global:${name}`);
if (this.global) resolved = this.global.resolve(route.name);
} else {
trace.push(`delegated:${name}`);
if (this.global) {
resolved = this.global.resolve(name);
if (!resolved) {
const tldResult = this.global.resolve(route.tld);
if (tldResult) resolved = tldResult;
}
}
}
if (!resolved) {
trace.push(`not_found:${name}`);
return {
result: null,
trace
};
}
if (resolved.startsWith("did:name:")) {
const nextName = resolved.replace(/^did:name:/, "");
return this._resolve(nextName, visited, trace, depth + 1);
}
return {
result: resolved,
trace
};
}
};
//#endregion
export { DIDNameResolver };