/**
 * 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` and `protocolPath` match by exact equality. `contextId` scopes
 * match a context subtree: the target context must equal the scoped context or
 * begin with the scoped context 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 scope.protocolPath === target.protocolPath;
        }
        if (scope.contextId !== undefined) {
            return PermissionScopeMatcher.matchesContextId(scope.contextId, target.contextId);
        }
        return true;
    }
    static matchesContextId(scopeContextId, candidateContextId) {
        return typeof candidateContextId === 'string' &&
            (candidateContextId === scopeContextId || candidateContextId.startsWith(scopeContextId + '/'));
    }
}
//# sourceMappingURL=permission-scope.js.map