/**
 * Shared matching rules for protocol-scoped permission grants.
 *
 * Matching fails closed for invalid combinations: `protocolPath` and
 * `contextId` are mutually exclusive, and either field requires `protocol`.
 * `protocol` matches by exact equality. `protocolPath` and `contextId` scopes
 * match subtrees: the target value must equal the scoped value or begin with the
 * scoped value followed by `/`.
 */
export class PermissionScopeMatcher {
    /**
     * Determines whether a candidate target is within a grant scope.
     */
    static matches(scope, target) {
        if (scope.protocolPath !== undefined && scope.contextId !== undefined) {
            return false;
        }
        if ((scope.protocolPath !== undefined || scope.contextId !== undefined) && scope.protocol === undefined) {
            return false;
        }
        if (scope.protocol !== undefined && scope.protocol !== target.protocol) {
            return false;
        }
        if (scope.protocolPath !== undefined) {
            return PermissionScopeMatcher.matchesSubtree(scope.protocolPath, target.protocolPath);
        }
        if (scope.contextId !== undefined) {
            return PermissionScopeMatcher.matchesContextId(scope.contextId, target.contextId);
        }
        return true;
    }
    static matchesContextId(scopeContextId, candidateContextId) {
        return PermissionScopeMatcher.matchesSubtree(scopeContextId, candidateContextId);
    }
    static matchesSubtree(scopeValue, candidateValue) {
        return typeof candidateValue === 'string' &&
            (candidateValue === scopeValue || candidateValue.startsWith(scopeValue + '/'));
    }
}
//# sourceMappingURL=permission-scope.js.map